Godot 15,706 (MIT, 4.7.2 of August 2026, github.com/godotengine/godot (https://github.com/godotengine/godot 117,876 ), about 117,800 stars) exports to the web from its editor with the Web export template. Its documentation is precise about the limits: Godot 4 targets only WebGL 2.0 through its Compatibility renderer, since Forward+ and Mobile need modern low-level APIs and "Godot currently does not support WebGPU"; C# projects cannot be exported to the web in Godot 4. Thread support needs SharedArrayBuffer, which means serving the page with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers; since Godot 4.3 a single-threaded export avoids that requirement.
PlayCanvas 62,178 is web-native: its engine (MIT, npm 2,036 install playcanvas, 2.22.4, github.com/playcanvas/engine (https://github.com/playcanvas/engine 16,941 ), about 16,900 stars) is "built on WebGL2 and WebGPU", and the PlayCanvas Editor runs in the browser with real-time collaboration. Publishing puts a build on PlayCanvas's free hosting or downloads it for self-hosting. The engine is free; Editor plans are Free (public projects, 1 GB), Personal at US$15 a month and Organization at US$50 per seat a month. Neither was run here, since both start from an editor.
<!doctype html>
<style>
body { margin: 0; font: 11px system-ui, sans-serif; background: #f7f4ee; color: #333; }
canvas { display: block; width: 100%; max-width: 600px; height: 280px; }
ul { max-width: 600px; margin: 6px 0; padding-left: 22px; font: 11px monospace; }
.yes { color: #2f6b2c; } .no { color: #a33b22; }
p { max-width: 600px; margin: 4px 6px; font: 11px monospace; }
</style>
<canvas id="c"></canvas>
<p id="out"></p>
<ul id="godot"></ul>
<script src="https://cdn.jsdelivr.net/npm/playcanvas@2.22.4/build/playcanvas.min.js"></script>
<script>
// PlayCanvas: an engine you can use without its editor, as a library.
const canvas = document.getElementById('c');
const app = new pc.Application(canvas, { graphicsDeviceOptions: { deviceTypes: ['webgl2'], antialias: true } });
app.setCanvasFillMode(pc.FILLMODE_NONE); app.setCanvasResolution(pc.RESOLUTION_AUTO);
app.resizeCanvas(canvas.clientWidth, 280);
const camera = new pc.Entity('camera');
camera.addComponent('camera', { clearColor: new pc.Color(0.93, 0.91, 0.87), fov: 35 });
camera.setPosition(1.6, 2.1, 5.6); camera.lookAt(0, 0.45, 0);
app.root.addChild(camera);
const sun = new pc.Entity('sun');
sun.addComponent('light', { type: 'directional', intensity: 1.2, castShadows: true, shadowBias: 0.2, normalOffsetBias: 0.05, shadowResolution: 2048, shadowDistance: 12 });
sun.setEulerAngles(50, 30, 0);
app.root.addChild(sun);
app.scene.ambientLight = new pc.Color(0.45, 0.43, 0.4);
function box(name, color, position, scale) {
const material = new pc.StandardMaterial(); material.diffuse = color; material.gloss = 0.3; material.update();
const entity = new pc.Entity(name);
entity.addComponent('render', { type: 'box', material, castShadows: true, receiveShadows: true });
entity.setPosition(...position); entity.setLocalScale(...scale);
app.root.addChild(entity); return entity;
}
box('shelf', new pc.Color(0.54, 0.38, 0.25), [0, -0.05, 0], [4, 0.1, 1.2]);
const colors = [[0.12, 0.37, 0.55], [0.36, 0.25, 0.6], [0.88, 0.6, 0.06], [0.25, 0.49, 0.23], [0.71, 0.27, 0.18], [0.16, 0.62, 0.56]];
const books = colors.map((c, i) => box('book' + i, new pc.Color(...c), [-1.5 + i * 0.6, 0.55, 0], [0.4, 1.1, 0.8]));
books[5].setEulerAngles(0, 0, 12); books[5].translate(-0.06, -0.02, 0); // one leaning book
app.on('update', (dt) => books[2].rotate(0, dt * 30, 0)); // one turning slowly
app.start();
document.getElementById('out').textContent = `PlayCanvas ${pc.version} | ${app.graphicsDevice.isWebGL2 ? 'WebGL 2' : app.graphicsDevice.deviceType}`;
// Godot 4 web export: WebGL 2 only (Compatibility renderer); threads need cross-origin isolation.
const gl2 = !!document.createElement('canvas').getContext('webgl2');
const isolated = self.crossOriginIsolated === true;
document.getElementById('godot').innerHTML = [
[gl2, 'WebGL 2: Godot 4 Compatibility renderer can run'],
[isolated, isolated ? 'crossOriginIsolated: threaded export works' : 'not crossOriginIsolated: use a single-threaded export (Godot 4.3+)'],
[typeof WebAssembly === 'object', 'WebAssembly available'],
].map(([ok, text]) => `<li class="${ok ? 'yes' : 'no'}">${ok ? 'yes' : 'no '} Godot: ${text}</li>`).join('');
</script>