# Godot HTML5 Export Best Practices Guide A comprehensive guide for exporting Godot games to HTML5/Web and deploying them properly. ## Table of Contents - [Pre-Export Checklist](#pre-export-checklist) - [Export Configuration](#export-configuration) - [Post-Export Optimization](#post-export-optimization) - [Deployment Best Practices](#deployment-best-practices) - [Common Issues & Solutions](#common-issues--solutions) - [Security Considerations](#security-considerations) --- ## Pre-Export Checklist ### 1. Set Project Icon **Location:** `Project → Project Settings → Application → Config → Icon` ``` ✅ Use a square PNG image (ideally 512x512 or 1024x1024) ✅ Godot will auto-generate smaller sizes (16x16, 32x32, etc.) ✅ This icon becomes your favicon and app icons ``` **Why:** Prevents 404 errors for `/favicon.ico` and provides proper branding. ### 2. Configure Export Presets **Location:** `Project → Export → Add → HTML5` #### Essential Settings: ``` Export Type: Regular (or Threads if you need SharedArrayBuffer) Export With Debug: OFF for production Texture Format: Compress for Smaller Downloads ``` #### Head Include (Optional): Only add custom meta tags if needed: ```html ``` ### 3. Audio Configuration **Location:** `Project → Project Settings → Audio` ``` ✅ Mix Rate: 44100 Hz (standard web audio) ✅ Output Latency: 15ms (balance between quality and performance) ✅ Enable Audio Input: Only if needed (requires user permissions) ``` ### 4. Display Settings **Location:** `Project → Project Settings → Display → Window` ``` ✅ Size: Set your target resolution (e.g., 1920x1080) ✅ Resizable: Enable if you want responsive sizing ✅ Stretch Mode: "2d" or "viewport" for web games ✅ Stretch Aspect: "expand" or "keep" based on your needs ``` --- ## Export Configuration ### Recommended HTML5 Export Settings #### 1. **Vram Texture Compression** Enable both formats for maximum compatibility: ``` ✅ For Desktop: s3tc (DXT1/DXT5) ✅ For Mobile: etc2 (or astc if targeting newer devices) ``` #### 2. **Custom HTML Shell** Leave default unless you need: - Custom loading screens - Analytics integration - Special PWA features #### 3. **Export Mode** - **Release:** Production builds (optimized, no debug symbols) - **Debug:** Development builds (larger, has debug info) ### 4. **Progressive Web App (PWA)** Enable if you want installable web apps: ``` Export → HTML5 → Progressive Web App: ON Offline Page: Enable for offline support Icon Sizes: Godot auto-generates these ``` --- ## Post-Export Optimization ### 1. File Size Optimization #### Compress WASM Files The `.wasm` file is usually the largest. Use Brotli or Gzip: ```bash # Using Brotli (best compression) brotli -9 YourGame.wasm # Using Gzip (better compatibility) gzip -9 YourGame.wasm ``` #### Nginx Configuration: ```nginx # Enable compression for Godot files gzip on; gzip_types application/wasm application/javascript text/html; gzip_min_length 1000; # Better: Use Brotli brotli on; brotli_types application/wasm application/javascript text/html; ``` ### 2. Caching Strategy ```nginx # Cache static assets aggressively location ~* \.(wasm|js|pck)$ { expires 1y; add_header Cache-Control "public, immutable"; } # Cache images location ~* \.(png|jpg|jpeg|gif|ico)$ { expires 1y; add_header Cache-Control "public, immutable"; } # Don't cache the HTML file location ~* \.html$ { expires -1; add_header Cache-Control "no-cache, no-store, must-revalidate"; } ``` ### 3. Required HTTP Headers For SharedArrayBuffer/Threads support: ```nginx add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Opener-Policy "same-origin" always; ``` **Note:** These are already in your nginx.conf - good job! --- ## Deployment Best Practices ### 1. Directory Structure ``` your-game/ ├── index.html # Main entry (or YourGame.html) ├── YourGame.js # Godot engine ├── YourGame.wasm # Compiled game ├── YourGame.pck # Game assets ├── YourGame.worker.js # Service worker ├── YourGame.icon.png # Favicon/Icon ├── favicon.ico # Browser favicon (convert from icon) ├── manifest.json # PWA manifest └── assets/ # Optional: separate assets folder ``` ### 2. Serving Files #### Option A: Simple HTTP Server (Development) ```bash # Python 3 python3 -m http.server 8000 # Python 2 python -m SimpleHTTPServer 8000 # Node.js (http-server) npx http-server -p 8000 ``` #### Option B: Production (Nginx/Caddy) Use your current Kubernetes deployment approach. ### 3. HTTPS Requirement Modern browsers require HTTPS for: - SharedArrayBuffer (threads) - Service Workers (offline) - WebGL 2.0 features - Clipboard API - Gamepad API **Your setup already has this via cert-manager + Let's Encrypt ✅** --- ## Common Issues & Solutions ### Issue 1: "SharedArrayBuffer is not defined" **Cause:** Missing CORS headers or HTTP (not HTTPS) **Solution:** ```nginx add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Opener-Policy "same-origin" always; ``` ### Issue 2: Blank Screen or "Failed to load" **Debug Steps:** 1. Open browser DevTools (F12) 2. Check Console for errors 3. Check Network tab for failed requests **Common Causes:** - Missing files (check all exports completed) - Wrong MIME types - File path mismatches (case-sensitive on Linux) **Solution:** Verify nginx MIME types: ```nginx types { application/wasm wasm; application/javascript js; text/html html; } ``` ### Issue 3: Audio Not Playing **Cause:** Browsers require user interaction before playing audio **Solution:** In Godot, show a "Click to Start" screen: ```gdscript func _ready(): # Don't autoplay audio # Wait for user input first pass func _on_start_button_pressed(): $AudioStreamPlayer.play() ``` ### Issue 4: Poor Performance **Optimization Tips:** 1. **Reduce Draw Calls:** Batch sprites, use TileMaps 2. **Optimize Shaders:** Simple shaders = better performance 3. **Texture Compression:** Use compressed textures 4. **Object Pooling:** Reuse nodes instead of creating/destroying 5. **Profile:** Use Godot's built-in profiler ### Issue 5: Large File Sizes **Solutions:** 1. **Enable Compression:** Gzip/Brotli on server 2. **Optimize Assets:** - Convert PNGs to WebP - Compress audio (OGG Vorbis instead of WAV) - Use texture compression 3. **Lazy Loading:** Load assets on-demand, not all at startup 4. **Asset Streaming:** Use Godot's asset streaming features --- ## Security Considerations ### 1. Content Security Policy (CSP) If you need CSP headers: ```nginx # Relaxed CSP for Godot games add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline'; worker-src 'self' blob:; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';" always; ``` ### 2. X-Frame-Options Prevent clickjacking: ```nginx add_header X-Frame-Options "SAMEORIGIN" always; ``` **Your nginx.conf already has this ✅** ### 3. Input Validation If your game has: - User-generated content - Multiplayer features - External data loading **Always validate and sanitize inputs!** --- ## Testing Checklist Before deploying to production: ### Browser Compatibility - [ ] Chrome/Chromium (Latest) - [ ] Firefox (Latest) - [ ] Safari (Latest) - [ ] Edge (Latest) - [ ] Mobile browsers (iOS Safari, Chrome Mobile) ### Performance Testing - [ ] Load time < 10 seconds on 3G - [ ] 60 FPS on target hardware - [ ] Memory usage stable over time - [ ] No memory leaks after extended play ### Functionality Testing - [ ] Audio works (after user interaction) - [ ] Input (keyboard, mouse, touch, gamepad) - [ ] Fullscreen mode works - [ ] Save/Load functionality - [ ] All game mechanics work as expected ### Accessibility - [ ] Works without sound (visual feedback) - [ ] Keyboard-only controls (if applicable) - [ ] Colorblind-friendly (if applicable) --- ## Advanced: Custom Docker Build For building in CI/CD (like your Kaniko setup): ### Dockerfile Example: ```dockerfile FROM nginx:alpine # Copy game files COPY htlm/ /usr/share/nginx/html/ # Copy custom nginx config COPY nginx.conf /etc/nginx/conf.d/default.conf # Set proper permissions RUN chown -R nginx:nginx /usr/share/nginx/html # Expose port EXPOSE 8080 # Health check HEALTHCHECK --interval=30s --timeout=3s \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 ``` ### nginx.conf (Your current one is excellent): ```nginx server { listen 8080; server_name _; root /usr/share/nginx/html; index YourGame.html; # Compression gzip on; gzip_types application/wasm application/javascript text/html; # CORS for SharedArrayBuffer add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Cross-Origin-Opener-Policy "same-origin" always; # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; # Caching location ~* \.(wasm|js|pck)$ { expires 1y; add_header Cache-Control "public, immutable"; } } ``` --- ## Resources ### Official Documentation - [Godot HTML5 Export Docs](https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_web.html) - [Godot Web Editor](https://editor.godotengine.org/) ### Tools - [Godot Web Export Validator](https://pwa-directory.appspot.com/) - [WebPageTest](https://www.webpagetest.org/) - Performance testing - [Lighthouse](https://developers.google.com/web/tools/lighthouse) - PWA audit ### Community - [Godot Forums - HTML5 Export](https://godotengine.org/community) - [r/godot](https://reddit.com/r/godot) --- ## Your Current Setup Analysis ### What You're Doing Right ✅ 1. **Proper CORS Headers** for SharedArrayBuffer 2. **Security Headers** (X-Frame-Options, X-Content-Type-Options) 3. **Compression** enabled (gzip) 4. **Proper MIME types** for .wasm files 5. **Cache headers** for static assets 6. **Service Worker** for offline support 7. **GitOps deployment** with Flux 8. **Container orchestration** with Kubernetes ### Recommendations 1. ✅ **Keep the favicon.ico** (converted from your icon) 2. ❌ **Don't create empty stub files** for scanner requests 3. ✅ **Monitor logs** but ignore 404s for files your app doesn't use 4. ✅ **Consider adding** a robots.txt to reduce scanner noise 5. ✅ **Consider adding** rate limiting to prevent abuse ### About Those 404 Errors The files being requested (`support_parent.css`, `twint_ch.js`, `lkk_ch.js`) are: - **Not part of Godot exports** - **Not needed by your game** - **Requested by bots/scanners** looking for e-commerce vulnerabilities - **Safe to ignore** - they don't affect your game **Best Practice:** Monitor real errors, ignore scanner noise in logs. --- ## License This guide is provided as-is for educational purposes. Feel free to adapt for your own projects. --- **Last Updated:** November 2025 **Godot Version:** 4.x (applicable to 3.x with minor changes)