Build & Deployment
AdvancedProduction build optimization, asset bundling, and deployment strategies for Web Engine applications.
Web Engine uses modern build tools (Vite, Turbo) for fast development and optimized production builds. This guide covers build configuration, asset optimization, and deployment to various hosting platforms.
Build System Overview#
Turborepo
Monorepo orchestration with intelligent caching
Vite
Lightning-fast dev server and optimized builds
Code Splitting
Automatic chunking for optimal loading
The monorepo structure uses Turborepo for build orchestration and Vite for application bundling:
{ "pipeline": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "dist/**"] }, "dev": { "cache": false, "persistent": true }, "lint": { "outputs": [] } }}Production Builds#
Build Commands#
# Build all packages and appsnpm run build # Build specific appnpm run build --filter=@web-engine-dev/studionpm run build --filter=@web-engine-dev/playernpm run build --filter=@web-engine-dev/docs # Build with specific packagesnpm run build:packages # Clean build cachenpm run cleanVite Build Configuration#
import { defineConfig } from "vite";import react from "@vitejs/plugin-react";import { getChunkName } from "@web-engine-dev/build-config"; export default defineConfig({ plugins: [react()], build: { target: "es2020", minify: "terser", sourcemap: true, rollupOptions: { output: { // Chunking strategy for optimal caching manualChunks: (id) => { return getChunkName(id); }, // Asset naming assetFileNames: "assets/[name]-[hash][extname]", chunkFileNames: "chunks/[name]-[hash].js", entryFileNames: "entries/[name]-[hash].js", }, }, terserOptions: { compress: { drop_console: true, // Remove console.log drop_debugger: true, // Remove debugger statements pure_funcs: ["console.debug", "console.trace"], }, }, }, // Asset optimization assetsInlineLimit: 4096, // Inline assets < 4KB // Dependencies to optimize optimizeDeps: { include: [ "three", "bitecs", "@dimforge/rapier3d-compat", ], },});Code Splitting Strategy#
Web Engine uses a strategic chunking system defined in @web-engine-dev/build-config:
export const chunkingStrategy = { vendor: [ "react", "react-dom", "three", "@react-three/fiber", "@react-three/drei", "zustand", ], physics: [ "@dimforge/rapier3d-compat", "@react-three/rapier", ], ui: [ "@radix-ui/react-slot", "@radix-ui/react-dialog", // ... all Radix UI components ],}; export function getChunkName(id: string): string | null { if (id.includes("node_modules")) { if (chunkingStrategy.vendor.some(v => id.includes(v))) return "vendor"; if (chunkingStrategy.physics.some(v => id.includes(v))) return "physics"; if (chunkingStrategy.ui.some(v => id.includes(v))) return "ui-libs"; return "deps"; } return null;}Chunk Benefits
Strategic chunking enables:
- Long-term caching of stable vendor code
- Parallel loading of independent chunks
- Smaller initial bundle size
- Faster incremental updates
Asset Optimization#
Texture Compression#
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader";import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; // Configure KTX2 loader for compressed texturesconst ktx2Loader = new KTX2Loader();ktx2Loader.setTranscoderPath("/basis/");ktx2Loader.detectSupport(renderer); // Configure DRACO loader for mesh compressionconst dracoLoader = new DRACOLoader();dracoLoader.setDecoderPath("/draco/"); // Use in GLTF loadergltfLoader.setKTX2Loader(ktx2Loader);gltfLoader.setDRACOLoader(dracoLoader);Model Optimization#
- gltfpack - Compress GLTF/GLB files with Draco/Meshopt
- Texture atlasing - Combine small textures into atlases
- LOD generation - Create Level of Detail meshes
- Mesh simplification - Reduce polygon count for distant objects
# Optimize GLTF with gltfpackgltfpack -i model.gltf -o model.glb -cc # Options:# -cc: Compress meshes with Draco# -tc: Compress textures with KTX2/Basis# -si 0.5: Simplify meshes to 50% trianglesEnvironment Variables#
# API ConfigurationVITE_API_URL=https://api.myengine.comVITE_PLAYER_URL=https://play.myengine.comVITE_DOCS_URL=https://docs.myengine.com # Feature FlagsVITE_ENABLE_ANALYTICS=trueVITE_ENABLE_MULTIPLAYER=trueVITE_ENABLE_ASSET_STREAMING=true # CDN ConfigurationVITE_CDN_URL=https://cdn.myengine.comVITE_ASSET_BASE_URL=https://assets.myengine.com # BrandingVITE_ENGINE_NAME=MyEngineVITE_ENGINE_VERSION=1.0.0// Access env variablesconst config = { apiUrl: import.meta.env.VITE_API_URL, playerUrl: import.meta.env.VITE_PLAYER_URL, enableAnalytics: import.meta.env.VITE_ENABLE_ANALYTICS === "true",};Deployment Targets#
Vercel (Recommended)#
{ "buildCommand": "turbo run build --filter=@web-engine-dev/studio", "outputDirectory": "apps/studio/.next", "framework": "nextjs", "rewrites": [ { "source": "/api/:path*", "destination": "https://api.myengine.com/:path*" } ], "headers": [ { "source": "/assets/(.*)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" } ] } ]}# Deploy to Vercelvercel --prod # Deploy specific appvercel --prod --cwd apps/studio # Environment variablesvercel env add VITE_API_URL productionNetlify#
[build] command = "turbo run build --filter=@web-engine-dev/player" publish = "apps/player/dist" [[redirects]] from = "/api/*" to = "https://api.myengine.com/:splat" status = 200 [[headers]] for = "/assets/*" [headers.values] Cache-Control = "public, max-age=31536000, immutable" [[headers]] for = "/*.js" [headers.values] Cache-Control = "public, max-age=31536000, immutable"Cloudflare Pages#
# Build commandturbo run build --filter=@web-engine-dev/docs # Output directoryapps/docs/out # Environment variablesNODE_VERSION=18CDN Configuration#
For optimal performance, serve static assets from a CDN:
// Configure asset base URLconst ASSET_CDN = import.meta.env.VITE_CDN_URL || ""; export function getAssetUrl(path: string): string { return `${ASSET_CDN}${path}`;} // Usageconst modelUrl = getAssetUrl("/models/character.glb");const textureUrl = getAssetUrl("/textures/grass.ktx2");CDN Best Practices
- Use long cache times (1 year) for versioned assets
- Enable compression (gzip/brotli) at the CDN level
- Configure CORS headers for cross-origin requests
- Use a CDN with global edge network for low latency
Production Checklist#
Pre-Deployment Checklist
- Build Optimization
- Enable minification and tree-shaking
- Remove console.log statements
- Generate sourcemaps for debugging
- Enable asset compression (gzip/brotli)
- Asset Optimization
- Compress textures to KTX2/Basis format
- Compress models with Draco/Meshopt
- Generate LOD meshes for large models
- Create texture atlases for UI elements
- Performance
- Test on low-end devices
- Profile with Chrome DevTools
- Ensure 60 FPS on target hardware
- Monitor memory usage
- Configuration
- Set production API URLs
- Configure CDN for assets
- Enable analytics and error tracking
- Test all environment variables
- Security
- Enable HTTPS everywhere
- Configure CSP headers
- Remove debug endpoints
- Validate all user inputs
Production Monitoring#
// Track performance metricsimport { PerformanceMonitor } from "@web-engine-dev/logging"; const perfMonitor = new PerformanceMonitor(logger); // Send metrics to analyticsfunction reportMetrics() { const snapshot = perfMonitor.getSnapshot(); analytics.track("performance", { fps: snapshot.fps.avg, frameTime: snapshot.frameTime.avg, heapUsed: snapshot.memory?.heapUsed, });} // Report every 30 secondssetInterval(reportMetrics, 30000);Monitor key metrics in production:
Error Tracking
Sentry, LogRocket, or similar for error monitoring
Analytics
Track user behavior and performance metrics
Performance
Core Web Vitals, FPS, load times
Uptime
Monitor API and CDN availability
Docker Deployment#
FROM node:18-alpine AS builder WORKDIR /app # Install dependenciesCOPY package*.json ./COPY turbo.json ./RUN npm ci # Copy sourceCOPY . . # BuildRUN npm run build --filter=@web-engine-dev/studio # Production imageFROM node:18-alpine WORKDIR /app COPY --from=builder /app/apps/studio/.next ./.nextCOPY --from=builder /app/node_modules ./node_modulesCOPY --from=builder /app/package.json ./package.json EXPOSE 3000 CMD ["npm", "start"]version: "3.8" services: studio: build: context: . dockerfile: apps/studio/Dockerfile ports: - "3000:3000" environment: - NODE_ENV=production - VITE_API_URL=https://api.myengine.com player: build: context: . dockerfile: apps/player/Dockerfile ports: - "5173:80" server: build: context: . dockerfile: apps/server/Dockerfile ports: - "4000:4000" environment: - NODE_ENV=production