Mobile Best Practices

Optimize your game and BZZE Ads integration for the best mobile experience and maximum revenue.

1. Responsive Design

Ensure your game works perfectly on all mobile screen sizes and orientations.

Viewport Configuration

HTML
<!-- Always include this in your HTML -->
<meta name="viewport" 
      content="width=device-width, 
               initial-scale=1.0, 
               maximum-scale=1.0, 
               user-scalable=no, 
               viewport-fit=cover">

CSS Media Queries

CSS
/* Mobile-first approach */
.game-container {
    width: 100%;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
}

/* Adjust for small phones */
@media (max-width: 375px) {
    .game-ui {
        font-size: 14px;
    }
}

/* Adjust for tablets */
@media (min-width: 768px) {
    .game-container {
        max-width: 768px;
        margin: 0 auto;
    }
}

/* Handle notches (iPhone X+) */
@supports (padding: max(0px)) {
    .game-header {
        padding-top: max(10px, env(safe-area-inset-top));
        padding-left: max(10px, env(safe-area-inset-left));
        padding-right: max(10px, env(safe-area-inset-right));
    }
}

Test Both Orientations

JavaScript
// Detect orientation changes
window.addEventListener('orientationchange', function() {
    console.log('Orientation:', screen.orientation.type);
    // Adjust game layout if needed
    resizeGame();
});

// Or force specific orientation in config.xml (Cordova/Capacitor)
// <preference name="orientation" value="portrait" />

2. Ad Integration Best Practices

Initialize Early

JavaScript
// Initialize as soon as DOM is ready
document.addEventListener('DOMContentLoaded', function() {
    RewardedAd.init({
        appId: "YOUR_APP_ID",
        apiKey: "YOUR_API_KEY",
        userId: getUserId(), // Persistent user ID
        autoPreload: true,   //  Preload ads automatically
        preloadCount: 2,     //  Keep 2 ads ready
        onReward: handleReward,
        onNoFill: handleNoFill,
        onError: handleError
    });
});

// Start preloading even before user clicks button
setTimeout(function() {
    console.log('Ads ready:', RewardedAd.isAdAvailable());
}, 3000);

Show Ads at Natural Breaks

JavaScript
//  GOOD: Show ads at natural game breaks
function onLevelComplete() {
    showLevelCompleteScreen();
    
    // Offer ad reward for bonus
    showAdButton("Watch ad for 2x rewards!");
}

function onGameOver() {
    showGameOverScreen();
    
    // Offer ad for continue
    showAdButton("Watch ad to continue?");
}

//  BAD: Don't interrupt gameplay
function onEnemySpawned() {
    RewardedAd.showAd(); // ← Never do this!
}

Provide Clear Value Proposition

JavaScript
//  Show what user will get
<button onclick="showRewardedAd()">
    🎁 Watch Ad for 500 Coins
</button>

//  Vague or no benefit
<button onclick="showRewardedAd()">
    Watch Video
</button>

Handle Network Issues Gracefully

JavaScript
RewardedAd.init({
    appId: "YOUR_APP_ID",
    apiKey: "YOUR_API_KEY",
    userId: getUserId(),
    
    onNoFill: function() {
        // No ads available
        showMessage("No ads available right now. Try again later!");
    },
    
    onError: function(error) {
        // Network or other error
        console.error('Ad error:', error);
        
        if (error.code === 'NETWORK_ERROR') {
            showMessage("Please check your internet connection");
        } else if (error.code === 'RATE_LIMIT_EXCEEDED') {
            showMessage("You've watched too many ads. Come back later!");
        } else {
            showMessage("Something went wrong. Please try again.");
        }
    }
});

// Check network status before showing ad
function showRewardedAd() {
    if (!navigator.onLine) {
        showMessage("Please connect to the internet to watch ads");
        return;
    }
    
    if (!RewardedAd.isAdAvailable()) {
        showMessage("Ads are loading... Please wait a moment");
        return;
    }
    
    RewardedAd.showAd();
}

3. Performance Optimization

Reduce Asset Sizes

Bash
# Compress images
# Use tools like TinyPNG, ImageOptim, or:
npm install -g imagemin-cli
imagemin assets/*.png --out-dir=assets/optimized

# Use WebP for better compression
# Convert images: cwebp input.png -o output.webp

# Minify JavaScript
npm install -g terser
terser game.js -o game.min.js -c -m

# Minify CSS
npm install -g clean-css-cli
cleancss -o style.min.css style.css

Lazy Load Resources

JavaScript
// Load game assets progressively
const gameAssets = {
    level1: ['bg1.jpg', 'player.png', 'enemy1.png'],
    level2: ['bg2.jpg', 'enemy2.png'],
    level3: ['bg3.jpg', 'boss.png']
};

function loadLevel(levelNum) {
    const assetsToLoad = gameAssets['level' + levelNum];
    
    assetsToLoad.forEach(asset => {
        const img = new Image();
        img.src = 'assets/' + asset;
    });
}

// Only load level 1 initially
loadLevel(1);

Optimize for Low-End Devices

JavaScript
// Detect device performance
function getDevicePerformance() {
    const memory = navigator.deviceMemory || 4; // GB
    const cores = navigator.hardwareConcurrency || 2;
    
    if (memory >= 4 && cores >= 4) {
        return 'high';
    } else if (memory >= 2 && cores >= 2) {
        return 'medium';
    } else {
        return 'low';
    }
}

// Adjust game quality
const performance = getDevicePerformance();

if (performance === 'low') {
    // Reduce particle effects
    particleSystem.maxParticles = 50;
    
    // Lower frame rate
    game.setFrameRate(30);
    
    // Disable shadows
    renderer.shadows = false;
} else if (performance === 'high') {
    particleSystem.maxParticles = 500;
    game.setFrameRate(60);
    renderer.shadows = true;
}

4. User Experience

Touch-Friendly Buttons

CSS
/* Minimum touch target size: 44x44px (Apple HIG) */
.game-button {
    min-width: 44px;
    min-height: 44px;
    padding: 12px 24px;
    font-size: 16px;
    border-radius: 8px;
    
    /* Prevent text selection on touch */
    -webkit-user-select: none;
    user-select: none;
    
    /* Prevent tap highlight */
    -webkit-tap-highlight-color: transparent;
}

/* Add visual feedback */
.game-button:active {
    transform: scale(0.95);
    opacity: 0.8;
}

Prevent Accidental Clicks

JavaScript
// Add confirmation for important actions
function showRewardedAd() {
    const confirmed = confirm(
        "Watch a 30-second ad to earn 500 coins?"
    );
    
    if (confirmed) {
        RewardedAd.showAd();
    }
}

// Or use custom modal
function showAdConfirmation() {
    showModal({
        title: "Watch Ad?",
        message: "Watch a short video to earn 500 coins",
        buttons: [
            { text: "Cancel", style: "secondary" },
            { text: "Watch Ad", style: "primary", onClick: showAd }
        ]
    });
}

Loading States

JavaScript
const adButton = document.getElementById('ad-button');

// Show loading state
adButton.addEventListener('click', function() {
    adButton.disabled = true;
    adButton.textContent = "Loading ad...";
    
    RewardedAd.showAd();
});

// Reset button after ad closes
RewardedAd.init({
    // ... other config
    onClose: function() {
        adButton.disabled = false;
        adButton.textContent = "Watch Ad for Reward";
    }
});

5. Data & Storage Management

Persist User ID

JavaScript
// Generate persistent user ID
function getUserId() {
    let userId = localStorage.getItem('bzze_user_id');
    
    if (!userId) {
        userId = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
        localStorage.setItem('bzze_user_id', userId);
    }
    
    return userId;
}

// Use in SDK init
RewardedAd.init({
    appId: "YOUR_APP_ID",
    apiKey: "YOUR_API_KEY",
    userId: getUserId() // ← Consistent across sessions
});

Handle Storage Quotas

JavaScript
// Check storage availability
if ('storage' in navigator && 'estimate' in navigator.storage) {
    navigator.storage.estimate().then(estimate => {
        const percentUsed = (estimate.usage / estimate.quota) * 100;
        console.log(`Storage used: ${percentUsed.toFixed(2)}%`);
        
        if (percentUsed > 80) {
            console.warn('Storage nearly full!');
            // Clean up old game saves or cached data
            cleanupOldData();
        }
    });
}

// Clear old data periodically
function cleanupOldData() {
    const keysToClean = [];
    
    for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        
        // Keep BZZE Ads data
        if (key.startsWith('bzze_')) continue;
        
        // Remove old game saves (example)
        if (key.startsWith('save_')) {
            const saveData = JSON.parse(localStorage.getItem(key));
            const age = Date.now() - saveData.timestamp;
            
            // Remove saves older than 30 days
            if (age > 30 * 24 * 60 * 60 * 1000) {
                keysToClean.push(key);
            }
        }
    }
    
    keysToClean.forEach(key => localStorage.removeItem(key));
}

6. Testing & Debugging

Enable Debug Mode

JavaScript
// Development build
const isDevelopment = window.location.hostname === 'localhost' || 
                      window.location.hostname === '127.0.0.1';

RewardedAd.init({
    appId: "YOUR_APP_ID",
    apiKey: "YOUR_API_KEY",
    userId: getUserId(),
    debug: isDevelopment // ← Auto-enable debug mode in dev
});

// Or manually enable for testing
if (isDevelopment) {
    RewardedAd.enableDebug();
    console.log('Debug mode enabled');
}

Test on Multiple Devices

Device Testing Checklist:

  • Small phone (iPhone SE, older Android)
  • Large phone (iPhone 14 Pro Max, Galaxy S23 Ultra)
  • Tablet (iPad, Android tablet)
  • 🔄 Portrait orientation
  • 🔄 Landscape orientation
  • 📶 Slow 3G connection (throttle in DevTools)
  • ✈️ Offline mode (airplane mode)
  • 🔋 Low battery mode (iOS)
  • 🌐 Different OS versions (iOS 14+, Android 8+)

Monitor Performance

JavaScript
// Track ad performance
let adMetrics = {
    shown: 0,
    completed: 0,
    revenue: 0
};

RewardedAd.init({
    appId: "YOUR_APP_ID",
    apiKey: "YOUR_API_KEY",
    userId: getUserId(),
    
    onAdShown: function() {
        adMetrics.shown++;
        console.log('Total ads shown:', adMetrics.shown);
    },
    
    onReward: function(reward) {
        adMetrics.completed++;
        adMetrics.revenue += 0.05; // Estimate
        
        console.log('Completion rate:', 
            (adMetrics.completed / adMetrics.shown * 100).toFixed(1) + '%');
    }
});

// Log metrics every 5 minutes
setInterval(function() {
    console.log('Ad Metrics:', adMetrics);
}, 5 * 60 * 1000);

7. Privacy & Compliance

Implement GDPR Consent (EU)

JavaScript
// Show consent dialog for EU users
function isEUUser() {
    const euCountries = ['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE'];
    const userCountry = getUserCountry(); // Detect from IP or user input
    return euCountries.includes(userCountry);
}

if (isEUUser() && !hasGivenConsent()) {
    showGDPRDialog();
}

function showGDPRDialog() {
    const consent = confirm(
        "We use ads to support this free game. " +
        "Do you consent to personalized ads? " +
        "(You can still play with non-personalized ads)"
    );
    
    localStorage.setItem('gdpr_consent', consent ? 'yes' : 'no');
    
    // Initialize SDK with consent flag
    RewardedAd.init({
        appId: "YOUR_APP_ID",
        apiKey: "YOUR_API_KEY",
        userId: getUserId(),
        gdprApplies: true,
        gdprConsent: consent
    });
}

Implement COPPA Compliance (Kids Apps)

JavaScript
// For apps targeting children under 13 (US)
RewardedAd.init({
    appId: "YOUR_APP_ID",
    apiKey: "YOUR_API_KEY",
    userId: getUserId(),
    coppaCompliant: true,      // ← Enable COPPA mode
    isChildDirected: true      // ← Flag as child-directed
});

// This ensures:
// - No personalized ads
// - No user tracking
// - COPPA-compliant ad networks only

See full compliance guides: GDPR | COPPA

8. App Store Guidelines

iOS App Store

  • Clearly disclose that your app contains ads
  • Set appropriate age rating based on ad content
  • Don't show ads that don't match your app's rating
  • Provide a way to restore purchases (if using IAP to remove ads)
  • Don't mislead users about ad frequency
  • Don't show ads that cover UI elements unexpectedly

Google Play Store

  • Comply with Google Play's ads policy
  • Disclose ads in your app description
  • Implement "Designed for Families" requirements if targeting kids
  • Don't show ads on lockscreen or notification bar
  • Don't use deceptive ad placements
  • Don't incentivize ad clicks (click fraud)

Quick Checklist

Before Launch:

  • Test on at least 3 different devices
  • Test both portrait and landscape
  • Test with slow/no internet connection
  • Verify ads show correctly
  • Verify rewards are granted
  • Check analytics in Publisher Dashboard
  • Implement GDPR consent (if targeting EU)
  • Implement COPPA compliance (if targeting kids)
  • Add "Contains Ads" to store listing
  • Set appropriate age rating
  • Test with debug mode disabled
  • Compress all assets
  • Remove console.log statements
  • Test payment flow (if selling ad removal)

Next Steps

Need Help?

Questions about mobile optimization? We're here to help!

View FAQ Contact Support