Files
neon-vortex/GODOT_HTML5_EXPORT_GUIDE.md
Neon Vortex 5b52eb9999
Some checks failed
Build and Push to Harbor / build-and-push (push) Has been cancelled
Add comprehensive Godot HTML5 export guide and remove stub files
- Created detailed best practices guide for Godot HTML5/Web exports
- Removed empty stub files (they mask problems rather than solving them)
- Kept favicon.ico as it's a legitimate browser requirement
- Guide covers export config, optimization, deployment, and troubleshooting
2025-11-22 14:34:00 -05:00

11 KiB

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

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:

<meta name="description" content="Your game description">
<meta name="keywords" content="game, webgl, godot">
<meta name="author" content="Your Name">

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

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:

# Using Brotli (best compression)
brotli -9 YourGame.wasm

# Using Gzip (better compatibility)
gzip -9 YourGame.wasm

Nginx Configuration:

# 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

# 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:

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)

# 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:

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:

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:

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:

# 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:

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:

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):

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

Tools

Community


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)