<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Cinevva Guides</title>
        <link>https://app.cinevva.com</link>
        <description>Longer-form guides for creators and players.</description>
        <lastBuildDate>Wed, 17 Jun 2026 11:20:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright 2024-present Cinevva</copyright>
        <item>
            <title><![CDATA[Steam Next Fest Strategy in 2026]]></title>
            <link>https://app.cinevva.com/guides/steam-next-fest-strategy-gamedeveloper</link>
            <guid>https://app.cinevva.com/guides/steam-next-fest-strategy-gamedeveloper</guid>
            <pubDate>Wed, 17 Jun 2026 11:16:21 GMT</pubDate>
            <description><![CDATA[
# Steam Next Fest Strategy in 2026

*What the data says about wishlists, demos, and timing. Real numbers from 200+ games.*

]]></description>
            <content:encoded><![CDATA[<h1 id="steam-next-fest-strategy-in-2026" tabindex="-1">Steam Next Fest Strategy in 2026 <a class="header-anchor" href="#steam-next-fest-strategy-in-2026" aria-label="Permalink to &quot;Steam Next Fest Strategy in 2026&quot;"></a></h1>
<p><em>What the data says about wishlists, demos, and timing. Real numbers from 200+ games.</em></p>
<hr>
<p>If you're planning around the calendar, Steam runs Next Fest three times in 2026: February 23 to March 2 (already done), June 15 to 22, and October 19 to 26. The June and October editions run from 10:00 AM PDT on the start day to 10:00 AM PDT on the end day, landing just before the Summer and Winter sales (February ran on Pacific time too, before daylight saving). A game can only join one Next Fest, ever, so pick the edition that fits your release window. Your demo has to be publicly playable by the time the fest opens, and you can't release until after it closes.</p>
<p>Here's the thing about Next Fest: everyone treats it like a lottery. Throw your demo in, cross your fingers, hope the algorithm smiles on you. That's not how it works.</p>
<p>I've been reading postmortems obsessively. Hundreds of them from 2025 alone. The pattern is so consistent it's almost boring: the games that &quot;win&quot; Next Fest aren't discovering success during the festival. They're amplifying momentum they already built.</p>
<p>Let me show you what I mean.</p>
<h2 id="the-formula-nobody-wants-to-hear" tabindex="-1">The Formula Nobody Wants to Hear <a class="header-anchor" href="#the-formula-nobody-wants-to-hear" aria-label="Permalink to &quot;The Formula Nobody Wants to Hear&quot;"></a></h2>
<p><strong>Wishlists Before × 2 = Wishlists Gained</strong></p>
<p>This ratio shows up in postmortem after postmortem.</p>
<p>Eilean Mor went into October 2025 with 2,117 wishlists. Gained 2,082 during the festival. Almost exactly 2×.</p>
<p>[IMAGE: Eilean Mor trailer screenshot or embed - <a href="https://www.youtube.com/watch?v=jFv7SjsQEws" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=jFv7SjsQEws</a>]</p>
<p>Dice of Kalma went in with 335 wishlists. Gained 659. Just under 2×. Their developer wrote: &quot;Our wishlists nearly tripled, which is awesome, but we have still lot of work to do.&quot;</p>
<p>That's the uncomfortable truth. Next Fest doubles what you already have. If you have nothing, you get nothing doubled.</p>
<h2 id="the-2-000-wishlist-cliff" tabindex="-1">The 2,000 Wishlist Cliff <a class="header-anchor" href="#the-2-000-wishlist-cliff" aria-label="Permalink to &quot;The 2,000 Wishlist Cliff&quot;"></a></h2>
<p>Data from 208 games in February 2025 shows a sharp inflection point at 2,000 wishlists.</p>
<table tabindex="0">
<thead>
<tr>
<th>Starting Wishlists</th>
<th>What Happens</th>
</tr>
</thead>
<tbody>
<tr>
<td>Under 1,000</td>
<td>You're invisible after day 2. Zero games jumped tiers.</td>
</tr>
<tr>
<td>1,000–2,000</td>
<td>Modest gains. You stay in your lane.</td>
</tr>
<tr>
<td>2,000+</td>
<td>The algorithm starts helping you. Real momentum possible.</td>
</tr>
<tr>
<td>10,000+</td>
<td>Gold tier. Strong gains likely.</td>
</tr>
<tr>
<td>100,000+</td>
<td>Diamond tier. You're competing for the top charts.</td>
</tr>
</tbody>
</table>
<p>Here's what a developer from that survey said: &quot;You really start to see how the wishlists a game earns takes off for games that have 2000+ wishlists before Steam Next Fest.&quot;</p>
<p>It's not an algorithm thing exactly. It's a validation thing. 2,000 wishlists means you've figured out something about marketing. You know how to reach people. That skill compounds during the festival.</p>
<h2 id="the-first-48-hours-are-everything" tabindex="-1">The First 48 Hours Are Everything <a class="header-anchor" href="#the-first-48-hours-are-everything" aria-label="Permalink to &quot;The First 48 Hours Are Everything&quot;"></a></h2>
<p>Valve gives everyone a shot on days 1 and 2. After that, the algorithm decides who deserves more visibility based on performance.</p>
<p>Bronze tier games? Their impressions fall off a cliff on day 3. Diamond tier games? Their impressions triple.</p>
<p><strong>Day 1-2:</strong> Push everything. Stream. Post. Coordinate with creators.
<strong>Day 3+:</strong> The algorithm has already decided. You're along for the ride.</p>
<h2 id="what-actually-won-in-2025" tabindex="-1">What Actually Won in 2025 <a class="header-anchor" href="#what-actually-won-in-2025" aria-label="Permalink to &quot;What Actually Won in 2025&quot;"></a></h2>
<h3 id="do-no-harm-february-2025" tabindex="-1">Do No Harm (February 2025) <a class="header-anchor" href="#do-no-harm-february-2025" aria-label="Permalink to &quot;Do No Harm (February 2025)&quot;"></a></h3>
<p>[IMAGE: Do No Harm trailer screenshot or embed - <a href="https://www.youtube.com/watch?v=GBlOHAL1LkY" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=GBlOHAL1LkY</a>]</p>
<p><strong>The numbers:</strong> 45,655 wishlists gained during Next Fest. Started with 52,102.</p>
<p><strong>What they did:</strong></p>
<ul>
<li>Trailer dropped on GameTrailers January 29 (weeks before Next Fest)</li>
<li>PR outreach timed with trailer</li>
<li>Demo went live February 4</li>
<li>Paid ads to amplify organic momentum</li>
</ul>
<p>This is the playbook. They didn't rely on Next Fest to discover them. They used Next Fest to multiply attention they'd already earned.</p>
<h3 id="cairn-october-2025" tabindex="-1">Cairn (October 2025) <a class="header-anchor" href="#cairn-october-2025" aria-label="Permalink to &quot;Cairn (October 2025)&quot;"></a></h3>
<p>[IMAGE: Cairn trailer screenshot or embed - <a href="https://www.youtube.com/watch?v=x6KWhvvk3bM" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=x6KWhvvk3bM</a>]</p>
<p>Hit #5 on Popular Upcoming on day 1. Top Demos and Trending Upcoming by day 2.</p>
<p><strong>What they did:</strong></p>
<ul>
<li>Demo launched December 2024 (10 months before their Next Fest)</li>
<li>32,000 followers before the festival</li>
<li>Accepted into 18 festivals before Next Fest</li>
<li>The Game Bakers (Furi, Haven) - established studio with track record</li>
</ul>
<p>They didn't need Next Fest. But they used it anyway because it's free amplification when you've already done the work.</p>
<h3 id="yapyap-october-2025" tabindex="-1">YAPYAP (October 2025) <a class="header-anchor" href="#yapyap-october-2025" aria-label="Permalink to &quot;YAPYAP (October 2025)&quot;"></a></h3>
<p>[IMAGE: YAPYAP gameplay screenshot or embed - <a href="https://www.youtube.com/watch?v=7d8Fd9XXAtQ" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=7d8Fd9XXAtQ</a>]</p>
<p>The &quot;friend slop&quot; phenomenon. Co-op horror where you play as wizards vandalizing a tower. Went viral on social media, then dominated Next Fest.</p>
<p>Their trailer dropped August 25, 2025 - one month before Next Fest. It earned 434K views. That was the inflection point. Everything after was just riding the wave.</p>
<h2 id="what-february-2026-confirmed" tabindex="-1">What February 2026 Confirmed <a class="header-anchor" href="#what-february-2026-confirmed" aria-label="Permalink to &quot;What February 2026 Confirmed&quot;"></a></h2>
<p>The February 2026 edition didn't change the playbook, it hardened it. The fest crossed 3,500 games, up 51% year over year, and the median title walked away with about 200 wishlists and 11 new followers. More games, thinner slices. The top 5% gained roughly 7,000 wishlists, down from about 10,000 a year earlier, so even the winners are splitting a more crowded pie. Co-op kept dominating, and the &quot;shadow-drop your demo right before the fest&quot; idea got debunked again: of the ten top earners analyzed, only one launched its demo just days before. The rest had demos out for a month or longer. Build the audience first, every edition keeps saying the same thing.</p>
<h2 id="the-genres-that-keep-winning" tabindex="-1">The Genres That Keep Winning <a class="header-anchor" href="#the-genres-that-keep-winning" aria-label="Permalink to &quot;The Genres That Keep Winning&quot;"></a></h2>
<p>From the February 2025 diamond tier:</p>
<ul>
<li><strong>Horror</strong> (×2) - Steam loves horror. Always has.</li>
<li><strong>Strategy/Management</strong> (×4) - &quot;Crafty-buildy-simulationy&quot; is the vibe</li>
<li><strong>Co-op &quot;friend slop&quot;</strong> - Lethal Company opened the floodgates</li>
<li><strong>Point-and-click</strong> (×2) - but both were sequels with existing audiences</li>
</ul>
<p>The point-and-click thing is interesting. Beholder: Conductor was the 4th game in its series. Duck Detective was a sequel. They had built-in audiences. First-time point-and-click devs don't see those results.</p>
<h2 id="what-the-small-games-say" tabindex="-1">What The Small Games Say <a class="header-anchor" href="#what-the-small-games-say" aria-label="Permalink to &quot;What The Small Games Say&quot;"></a></h2>
<p>Not everyone is chasing diamond tier. Here's what developers at the smaller end reported from October 2025:</p>
<blockquote>
<p>&quot;62% conversion rate from visits to wishlists. Store page itself seems to work. But impressions were high and people didn't click. Capsule art needs an update.&quot;</p>
</blockquote>
<blockquote>
<p>&quot;57% of store visitors played our demo. That sounds high? But only 15% who played also wishlisted. Something didn't meet expectations.&quot;</p>
</blockquote>
<blockquote>
<p>&quot;Got permanently banned from r/cozygames for asking if they found our game cozy. Always try to follow the rules but this was pretty surprising.&quot;</p>
</blockquote>
<p>The grind is real. These developers are testing capsule art, tweaking tags, getting banned from subreddits. It's not glamorous.</p>
<p>One pattern that's gotten stronger: co-op &quot;friend slop&quot; games rack up huge demo play counts but convert fewer of those players into wishlists, because the audience showing up is casual and there to mess around with friends, not to track a release. High plays don't automatically mean high wishlists.</p>
<h2 id="the-honest-playbook" tabindex="-1">The Honest Playbook <a class="header-anchor" href="#the-honest-playbook" aria-label="Permalink to &quot;The Honest Playbook&quot;"></a></h2>
<p><strong>Months before Next Fest:</strong></p>
<ol>
<li>Launch your demo early. Like, way early. The winners had demos out for 6-12 months.</li>
<li>Enter smaller genre festivals first. Use them as polish runs.</li>
<li>Build to 2,000+ wishlists. That's your real threshold.</li>
</ol>
<p><strong>During Next Fest:</strong></p>
<ol>
<li>Days 1-2 are make or break. Push everything.</li>
<li>Stream if you can. It shows as &quot;live&quot; on your page.</li>
<li>Coordinate with streamers. Pay them if you have to.</li>
<li>Put your demo on a separate Steam page so it can collect reviews.</li>
</ol>
<p><strong>After Next Fest:</strong></p>
<ol>
<li>Don't launch immediately. A few weeks later actually performs better.</li>
<li>Use the momentum for your actual launch marketing.</li>
</ol>
<h2 id="when-to-skip-it" tabindex="-1">When To Skip It <a class="header-anchor" href="#when-to-skip-it" aria-label="Permalink to &quot;When To Skip It&quot;"></a></h2>
<p>Real talk: Next Fest isn't for everyone.</p>
<p>Skip it if:</p>
<ul>
<li>You have under 1,000 wishlists (you'll be invisible by day 3)</li>
<li>Your demo isn't polished (you get one shot at first impressions)</li>
<li>It's your first festival ever (do smaller ones first)</li>
<li>You're launching immediately after (wait a few weeks)</li>
</ul>
<p>There are dozens of smaller festivals every month. More focused audiences. Less competition. Check howtomarketagame.com/festivals for the list.</p>
<p>Next Fest is the default because Valve prompts everyone to join. But the best marketing isn't the stuff everyone gets prompted to do.</p>
<hr>
<h2 id="about-the-author" tabindex="-1">About the Author <a class="header-anchor" href="#about-the-author" aria-label="Permalink to &quot;About the Author&quot;"></a></h2>
<p>Oleg Sidorkin builds tools for indie game developers. He's been studying game marketing data and developer postmortems to understand what actually works versus what gets hyped.</p>
<hr>
<p><strong>Notes for Game Developer editors:</strong></p>
<p>IMAGE PLACEMENTS:</p>
<ol>
<li>After &quot;Almost exactly 2×&quot; paragraph - Eilean Mor video embed or screenshot</li>
<li>After &quot;Do No Harm (February 2025)&quot; header - Do No Harm video embed or screenshot</li>
<li>After &quot;Cairn (October 2025)&quot; header - Cairn video embed or screenshot</li>
<li>After &quot;YAPYAP (October 2025)&quot; header - YAPYAP video embed or screenshot</li>
</ol>
<p>VIDEO URLS:</p>
<ul>
<li>Eilean Mor: <a href="https://www.youtube.com/watch?v=jFv7SjsQEws" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=jFv7SjsQEws</a></li>
<li>Do No Harm: <a href="https://www.youtube.com/watch?v=GBlOHAL1LkY" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=GBlOHAL1LkY</a></li>
<li>Cairn: <a href="https://www.youtube.com/watch?v=x6KWhvvk3bM" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=x6KWhvvk3bM</a></li>
<li>YAPYAP: <a href="https://www.youtube.com/watch?v=7d8Fd9XXAtQ" target="_blank" rel="noreferrer">https://www.youtube.com/watch?v=7d8Fd9XXAtQ</a></li>
</ul>
<p>STEAM LINKS (for reference/linking):</p>
<ul>
<li>Eilean Mor: <a href="https://store.steampowered.com/app/3871570/Eilean_Mor_The_Lost_Keepers/" target="_blank" rel="noreferrer">https://store.steampowered.com/app/3871570/Eilean_Mor_The_Lost_Keepers/</a></li>
<li>Do No Harm: <a href="https://store.steampowered.com/app/3138780/Do_No_Harm/" target="_blank" rel="noreferrer">https://store.steampowered.com/app/3138780/Do_No_Harm/</a></li>
<li>Cairn: <a href="https://store.steampowered.com/app/1588550/Cairn/" target="_blank" rel="noreferrer">https://store.steampowered.com/app/1588550/Cairn/</a></li>
<li>YAPYAP: <a href="https://store.steampowered.com/app/3834090/YAPYAP/" target="_blank" rel="noreferrer">https://store.steampowered.com/app/3834090/YAPYAP/</a></li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unity Alternatives for Web Games (2026)]]></title>
            <link>https://app.cinevva.com/guides/unity-alternatives-web-games</link>
            <guid>https://app.cinevva.com/guides/unity-alternatives-web-games</guid>
            <pubDate>Thu, 11 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The best Unity alternatives for browser games in 2026 - Godot, PlayCanvas, Three.js, Defold, Cocos, Phaser, and more, compared on web export, build size, language, and cost, with a pick for why you're leaving Unity.]]></description>
            <content:encoded><![CDATA[<h1 id="unity-alternatives-for-web-games-2026" tabindex="-1">Unity Alternatives for Web Games (2026) <a class="header-anchor" href="#unity-alternatives-for-web-games-2026" aria-label="Permalink to &quot;Unity Alternatives for Web Games (2026)&quot;"></a></h1>
<figure style="margin:1.5rem 0">
<img src="/img/guides/unity-alternatives-web-hero.svg" alt="Unity alternatives for web games in 2026" style="width:100%;border-radius:10px">
</figure>
<p>Unity can export to the web, but its WebGL builds are heavy, its pricing has whiplashed developers since 2023, and for a browser-first game it's rarely the lightest path. If you're looking for a Unity alternative specifically for web games, this guide covers the engines that actually ship small, fast browser builds in 2026, and which one to pick based on why you're leaving Unity.</p>
<p>If you want the whole field side by side, start with our <a href="/guides/web-game-engines-comparison.html">best web game engines for 2026 comparison</a>. This page is for the specific case of replacing Unity for the web.</p>
<h2 id="why-developers-look-for-unity-alternatives" tabindex="-1">Why developers look for Unity alternatives <a class="header-anchor" href="#why-developers-look-for-unity-alternatives" aria-label="Permalink to &quot;Why developers look for Unity alternatives&quot;"></a></h2>
<p>A few reasons come up again and again:</p>
<ul>
<li><strong>Pricing trust.</strong> The <a href="/news/2024-09-12-unity-runtime-fee-cancelled.html">Unity Runtime Fee</a> was cancelled in September 2024 and hasn't returned, but it taught a lot of teams to plan around the question &quot;what if the terms change again?&quot; An engine whose license can't be repriced removes that risk.</li>
<li><strong>Web build size.</strong> Unity's empty WebGL builds start around 8 to 11 MB. Web-native engines ship in the 1 to 5 MB range, which players feel directly in load time.</li>
<li><strong>Mobile browser support.</strong> Unity WebGL on phones has historically been fragile. Lighter engines built for the web behave better on mobile Safari and Chrome.</li>
<li><strong>Open source.</strong> Some teams want a codebase they can read, fork, and depend on without a vendor.</li>
</ul>
<p>The good news: for web, the alternatives are strong, and several are free forever.</p>
<h2 id="quick-comparison" tabindex="-1">Quick comparison <a class="header-anchor" href="#quick-comparison" aria-label="Permalink to &quot;Quick comparison&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Alternative</th>
<th>Type</th>
<th>Web tech</th>
<th>Build size (empty)</th>
<th>Language</th>
<th>Cost</th>
<th>Best for</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="/guides/godot-vs-unity-web-games.html"><strong>Godot</strong></a></td>
<td>Full 2D/3D engine</td>
<td>WebGL2 + Wasm</td>
<td>~5 MB Brotli</td>
<td>GDScript (C# not on web yet)</td>
<td>Free (MIT)</td>
<td>The closest all-round Unity replacement</td>
</tr>
<tr>
<td><strong>PlayCanvas</strong></td>
<td>Web-first 3D engine</td>
<td>WebGL2 + WebGPU</td>
<td>~1-2 MB</td>
<td>JavaScript</td>
<td>Free tier; paid plans</td>
<td>Browser-first 3D with a cloud editor</td>
</tr>
<tr>
<td><strong>Three.js / Cinevva</strong></td>
<td>3D library / engine</td>
<td>WebGL2 + WebGPU</td>
<td>~150 KB core</td>
<td>JavaScript / TypeScript</td>
<td>Free (MIT)</td>
<td>Maximum control, smallest footprint</td>
</tr>
<tr>
<td><strong>Defold</strong></td>
<td>Web-first 2D/3D</td>
<td>WebGL + Wasm</td>
<td>~1.14 MB</td>
<td>Lua</td>
<td>Free</td>
<td>Smallest, fastest-loading web games</td>
</tr>
<tr>
<td><strong>Cocos Creator</strong></td>
<td>Full 2D/3D engine</td>
<td>WebGL + WebGPU + Wasm</td>
<td>~2-4 MB</td>
<td>TypeScript</td>
<td>Free</td>
<td>Mini-games and the Asian web market</td>
</tr>
<tr>
<td><strong>Phaser</strong></td>
<td>2D framework</td>
<td>WebGL/Canvas</td>
<td>~500 KB</td>
<td>JavaScript</td>
<td>Free (MIT)</td>
<td>2D web games, biggest HTML5 community</td>
</tr>
<tr>
<td><strong>Construct / GDevelop</strong></td>
<td>Visual 2D</td>
<td>WebGL</td>
<td>~1-3 MB</td>
<td>No-code</td>
<td>Subscription / free tier</td>
<td>Leaving Unity without writing code</td>
</tr>
</tbody>
</table>
<p>There's no single &quot;Unity replacement.&quot; The right pick depends on whether you need 3D, whether you write code, and why Unity wasn't working for you.</p>
<h2 id="godot-the-closest-all-round-replacement" tabindex="-1">Godot: the closest all-round replacement <a class="header-anchor" href="#godot-the-closest-all-round-replacement" aria-label="Permalink to &quot;Godot: the closest all-round replacement&quot;"></a></h2>
<p>Godot is the alternative most ex-Unity developers land on. It's a full 2D and 3D engine, free under the MIT license with no seats, royalties, or revenue caps, and its web export is solid. Build size is around 5 MB compressed with Brotli, smaller than Unity's by default. Since Godot 4.3 you can export single-threaded web builds that drop the SharedArrayBuffer requirement, which also fixed the old iOS and Safari playback problems.</p>
<p>The one catch for web: Godot's C# (.NET) projects can't export to the browser yet, only GDScript does. If you're moving a C# codebase, that matters. We cover the trade-offs in detail in <a href="/guides/godot-vs-unity-web-games.html">Godot vs Unity for web games</a>.</p>
<p>Pick Godot if you want the most Unity-like all-round engine, free forever, with a clean 2D and 3D web path.</p>
<h2 id="playcanvas-web-first-3d-with-an-editor" tabindex="-1">PlayCanvas: web-first 3D with an editor <a class="header-anchor" href="#playcanvas-web-first-3d-with-an-editor" aria-label="Permalink to &quot;PlayCanvas: web-first 3D with an editor&quot;"></a></h2>
<p>PlayCanvas was built for the browser from day one. Its runtime is roughly 1 to 2 MB, it supports both WebGL2 and WebGPU, and it has a collaborative cloud editor that feels familiar if you liked Unity's scene workflow. Games are scripted in JavaScript. It's a strong choice when you need real-time 3D in the browser and want a visual editor rather than a code-only library.</p>
<p>Pick PlayCanvas if you want Unity-style 3D scene editing but a tiny web build.</p>
<h2 id="three-js-and-cinevva-maximum-control-smallest-footprint" tabindex="-1">Three.js and Cinevva: maximum control, smallest footprint <a class="header-anchor" href="#three-js-and-cinevva-maximum-control-smallest-footprint" aria-label="Permalink to &quot;Three.js and Cinevva: maximum control, smallest footprint&quot;"></a></h2>
<p>If you want the lightest possible web build and full control over rendering, <a href="/guides/playcanvas-vs-threejs.html">Three.js</a> is the standard. The core library is around 150 KB and it powers a huge share of 3D on the web. It's a rendering library rather than a full engine, so you bring your own game systems or a framework on top.</p>
<p>That's exactly why we built <a href="/engine.html">Cinevva Engine</a> on Three.js: open-source, web-first, with the game systems layered on so you don't start from an empty canvas. One codebase ships to the browser, mobile, desktop, Steam, and Discord. No seats, no install fees, no terms that can change after you've built your game, which is the thing the Runtime Fee episode made people care about.</p>
<p>Pick Three.js or Cinevva if you want web-native 3D, open source, and the smallest footprint, and you're comfortable in JavaScript or TypeScript.</p>
<h2 id="defold-the-smallest-fastest-2d-web-builds" tabindex="-1">Defold: the smallest, fastest 2D web builds <a class="header-anchor" href="#defold-the-smallest-fastest-2d-web-builds" aria-label="Permalink to &quot;Defold: the smallest, fastest 2D web builds&quot;"></a></h2>
<p>Defold ships the smallest web builds of any full engine, around 1.14 MB, and loads fast. It does 2D and lighter 3D, scripts in Lua, and is free with no revenue thresholds. If your Unity game is 2D and load time is the priority, Defold is hard to beat on the web.</p>
<p>Pick Defold for casual and mobile web games where every kilobyte counts.</p>
<h2 id="cocos-creator-mini-games-and-the-asian-market" tabindex="-1">Cocos Creator: mini-games and the Asian market <a class="header-anchor" href="#cocos-creator-mini-games-and-the-asian-market" aria-label="Permalink to &quot;Cocos Creator: mini-games and the Asian market&quot;"></a></h2>
<p>Cocos Creator is a full 2D and 3D engine with WebGL, WebGPU, and Wasm support, builds in the 2 to 4 MB range, and scripts in TypeScript. It's especially strong for mini-game platforms (WeChat, TikTok, Taobao) where Unity's size is a non-starter. It's free.</p>
<p>Pick Cocos Creator for mini-games or if you're targeting the Asian web ecosystem.</p>
<h2 id="phaser-the-2d-web-standard" tabindex="-1">Phaser: the 2D web standard <a class="header-anchor" href="#phaser-the-2d-web-standard" aria-label="Permalink to &quot;Phaser: the 2D web standard&quot;"></a></h2>
<p>If your game is 2D and you write JavaScript, Phaser is the most popular HTML5 game framework, with the largest community and ecosystem. Builds are around 500 KB. It's free under the MIT license. For 2D web games it's often a better fit than Unity ever was. See <a href="/guides/best-2d-game-engines-2026.html">best 2D game engines for 2026</a> for how it compares to Defold, Construct, and GDevelop.</p>
<p>Pick Phaser for 2D web games when you want code, community, and a small build.</p>
<h2 id="construct-and-gdevelop-leaving-unity-without-code" tabindex="-1">Construct and GDevelop: leaving Unity without code <a class="header-anchor" href="#construct-and-gdevelop-leaving-unity-without-code" aria-label="Permalink to &quot;Construct and GDevelop: leaving Unity without code&quot;"></a></h2>
<p>If part of why Unity frustrated you was the amount of code and setup, the visual engines are a real alternative. <a href="/guides/construct-vs-gdevelop.html">Construct and GDevelop</a> both build 2D games with no programming and export clean web builds. GDevelop has a free tier and is open-source-friendly; Construct is subscription-based with a polished editor.</p>
<p>Pick one of these if you want to build web games visually with little or no code.</p>
<h2 id="how-to-choose-by-reason-for-leaving-unity" tabindex="-1">How to choose, by reason for leaving Unity <a class="header-anchor" href="#how-to-choose-by-reason-for-leaving-unity" aria-label="Permalink to &quot;How to choose, by reason for leaving Unity&quot;"></a></h2>
<ul>
<li><strong>Left over pricing or trust?</strong> Go open source: Godot, Defold, Phaser, Three.js, or Cinevva. None can be repriced out from under you.</li>
<li><strong>Need 3D in the browser?</strong> PlayCanvas (with an editor) or Three.js and Cinevva (for control and the smallest size).</li>
<li><strong>Building 2D?</strong> Defold for the smallest builds, Phaser for the biggest community.</li>
<li><strong>Don't want to code?</strong> Construct or GDevelop.</li>
<li><strong>Targeting mini-game platforms?</strong> Cocos Creator.</li>
</ul>
<p>Whatever you pick, the web rewards small builds and fast loads. Once you've chosen, see <a href="/tutorials/ship-web-game-fast.html">ship a web game that loads fast</a>.</p>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="what-is-the-best-unity-alternative-for-web-games" tabindex="-1">What is the best Unity alternative for web games? <a class="header-anchor" href="#what-is-the-best-unity-alternative-for-web-games" aria-label="Permalink to &quot;What is the best Unity alternative for web games?&quot;"></a></h3>
<p>For most teams it's Godot: a free, open-source, full 2D and 3D engine with smaller web builds than Unity and a clean single-threaded export path for iOS and Safari. If you specifically need browser-first 3D, PlayCanvas or Three.js are lighter still. The best pick depends on whether you need 3D and whether you write code.</p>
<h3 id="is-there-a-free-alternative-to-unity" tabindex="-1">Is there a free alternative to Unity? <a class="header-anchor" href="#is-there-a-free-alternative-to-unity" aria-label="Permalink to &quot;Is there a free alternative to Unity?&quot;"></a></h3>
<p>Yes, several. Godot, Defold, Phaser, PixiJS, Excalibur.js, and Three.js are all free and open-source with no revenue caps or seat fees. Cocos Creator and GDevelop also have free options. For web games specifically, these free engines often ship smaller, faster builds than Unity.</p>
<h3 id="why-switch-from-unity-for-web-games" tabindex="-1">Why switch from Unity for web games? <a class="header-anchor" href="#why-switch-from-unity-for-web-games" aria-label="Permalink to &quot;Why switch from Unity for web games?&quot;"></a></h3>
<p>The common reasons are build size (Unity's empty WebGL builds start around 8 to 11 MB versus 1 to 5 MB for web-native engines), mobile browser reliability, pricing trust after the 2023 Runtime Fee episode, and a preference for open source. If web is your main target, a web-first engine usually gives a better result.</p>
<h3 id="can-i-replace-unity-with-an-open-source-engine" tabindex="-1">Can I replace Unity with an open-source engine? <a class="header-anchor" href="#can-i-replace-unity-with-an-open-source-engine" aria-label="Permalink to &quot;Can I replace Unity with an open-source engine?&quot;"></a></h3>
<p>Yes. Godot is the closest open-source equivalent for general 2D and 3D, and it's MIT licensed with no fees. For web-native 3D, Three.js (and engines built on it like Cinevva) are open source too. The main migration cost is rebuilding in a new language, since each engine has its own scripting.</p>
<h3 id="which-unity-alternative-has-the-smallest-web-build" tabindex="-1">Which Unity alternative has the smallest web build? <a class="header-anchor" href="#which-unity-alternative-has-the-smallest-web-build" aria-label="Permalink to &quot;Which Unity alternative has the smallest web build?&quot;"></a></h3>
<p>Among full engines, Defold is the smallest at about 1.14 MB. Three.js as a library is even smaller, with a core around 150 KB, though you add your own game code. Godot lands around 5 MB compressed, still well under Unity's 8 MB-plus starting point.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/web-game-engines-comparison.html">Best Web Game Engines for 2026 (Compared)</a> — the full field, ranked</li>
<li><a href="/guides/godot-vs-unity-web-games.html">Godot vs Unity for Web Games</a> — the closest alternative, head to head</li>
<li><a href="/news/2024-09-12-unity-runtime-fee-cancelled.html">Unity Runtime Fee Cancelled: the official story</a> — why pricing trust became a deciding factor</li>
<li><a href="/guides/best-2d-game-engines-2026.html">Best 2D Game Engines for 2026</a> — if your game is 2D</li>
<li><a href="/guides/playcanvas-vs-threejs.html">PlayCanvas vs Three.js</a> — engine versus library for web 3D</li>
<li><a href="/engine.html">Cinevva Engine</a> — our open-source, web-first engine built on Three.js</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Best 2D Game Engines for 2026 (Compared)]]></title>
            <link>https://app.cinevva.com/guides/best-2d-game-engines-2026</link>
            <guid>https://app.cinevva.com/guides/best-2d-game-engines-2026</guid>
            <pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The best 2D game engines for 2026 - Godot, GameMaker, Construct, Defold, Phaser, GDevelop, Unity, and LÖVE, compared by price, web export, build size, and who each one is for.]]></description>
            <content:encoded><![CDATA[<h1 id="best-2d-game-engines-for-2026-compared" tabindex="-1">Best 2D Game Engines for 2026 (Compared) <a class="header-anchor" href="#best-2d-game-engines-for-2026-compared" aria-label="Permalink to &quot;Best 2D Game Engines for 2026 (Compared)&quot;"></a></h1>
<figure style="margin:1.5rem 0">
<img src="/img/guides/best-2d-game-engines-2026-hero.svg" alt="Best 2D game engines for 2026: Godot, GameMaker, Construct, Defold, Phaser" style="width:100%;border-radius:10px">
</figure>
<p>2D is alive and well in 2026, and you've got more good engines than ever. The right one depends on whether you code or not, whether you're shipping to the web, and how small you need the build. Here's how the main options compare, and who each one is for.</p>
<p>If you're weighing 3D or full cross-platform engines too, see the broader <a href="/guides/web-game-engines-comparison.html">web game engines comparison</a>.</p>
<h2 id="quick-reference" tabindex="-1">Quick Reference <a class="header-anchor" href="#quick-reference" aria-label="Permalink to &quot;Quick Reference&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Engine</th>
<th>Price</th>
<th>Web export</th>
<th>Best for</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://godotengine.org/" target="_blank" rel="noreferrer"><strong>Godot</strong></a></td>
<td>Free (MIT)</td>
<td>Good (WebGL2, GDScript)</td>
<td>Best all-round and open-source pick</td>
</tr>
<tr>
<td><a href="https://gamemaker.io/" target="_blank" rel="noreferrer"><strong>GameMaker</strong></a></td>
<td>Free non-commercial; $99.99 one-time Pro</td>
<td>HTML5 included</td>
<td>Commercial 2D, fast shipping</td>
</tr>
<tr>
<td><a href="https://www.construct.net/" target="_blank" rel="noreferrer"><strong>Construct</strong></a></td>
<td>Free tier; ~$5-$39/mo</td>
<td>Excellent (HTML5-native)</td>
<td>Beginners and no-code, web-first</td>
</tr>
<tr>
<td><a href="https://defold.com/" target="_blank" rel="noreferrer"><strong>Defold</strong></a></td>
<td>Free</td>
<td>Excellent (&lt;2 MB gzip)</td>
<td>Smallest, fastest-loading web builds</td>
</tr>
<tr>
<td><a href="https://phaser.io/" target="_blank" rel="noreferrer"><strong>Phaser</strong></a></td>
<td>Free (MIT)</td>
<td>Native (it's a JS framework)</td>
<td>Code-first web games</td>
</tr>
<tr>
<td><a href="https://gdevelop.io/" target="_blank" rel="noreferrer"><strong>GDevelop</strong></a></td>
<td>Free; premium from $5/mo</td>
<td>Yes (HTML5)</td>
<td>Free no-code</td>
</tr>
<tr>
<td><a href="https://unity.com/" target="_blank" rel="noreferrer"><strong>Unity</strong></a></td>
<td>Personal free under $200K; Pro ~$2,200/yr</td>
<td>Yes (WebGL2)</td>
<td>Big ecosystem, cross-platform reach</td>
</tr>
<tr>
<td><a href="https://love2d.org/" target="_blank" rel="noreferrer"><strong>LÖVE</strong></a></td>
<td>Free (open-source)</td>
<td>Via love.js (third-party)</td>
<td>Minimalist Lua prototyping</td>
</tr>
</tbody>
</table>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/a_TDD1QhYGg" title="Unity vs Godot vs Defold! Game Engine Comparison" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Asatte Games compares three of the engines below head to head.</div>
<h2 id="the-engines" tabindex="-1">The Engines <a class="header-anchor" href="#the-engines" aria-label="Permalink to &quot;The Engines&quot;"></a></h2>
<h3 id="godot" tabindex="-1">Godot <a class="header-anchor" href="#godot" aria-label="Permalink to &quot;Godot&quot;"></a></h3>
<p>The open-source favorite, and a genuinely excellent 2D engine. Godot 4.5 (September 2025) has a dedicated 2D renderer, a lightweight editor, and beginner-friendly GDScript. It's free under the MIT license with no royalties. Web export is solid on WebGL2, with the caveat that only GDScript exports to the browser, not C#. If you want one free tool that does great 2D and decent 3D, this is the default pick.</p>
<p><strong>Best for:</strong> all-round 2D, open-source projects, pixel art.</p>
<h3 id="gamemaker" tabindex="-1">GameMaker <a class="header-anchor" href="#gamemaker" aria-label="Permalink to &quot;GameMaker&quot;"></a></h3>
<p>The engine behind hits like Undertale and a long line of commercial 2D games. It uses GML, its own fast scripting language, and ships HTML5 export even on the free tier. Pricing changed to a friendly model: free for non-commercial use and a one-time $99.99 Pro license for commercial work, instead of a subscription.</p>
<p><strong>Best for:</strong> commercial 2D where you want to ship fast, pixel art.</p>
<h3 id="construct" tabindex="-1">Construct <a class="header-anchor" href="#construct" aria-label="Permalink to &quot;Construct&quot;"></a></h3>
<p>A browser-based, no-code engine built around an event system. You build games visually with no programming, and because it's HTML5-native, web export is excellent. Pricing is subscription-based, roughly $5 to $39 a month depending on tier, with a limited free option.</p>
<p><strong>Best for:</strong> beginners, no-code creators, and web-first 2D.</p>
<h3 id="defold" tabindex="-1">Defold <a class="header-anchor" href="#defold" aria-label="Permalink to &quot;Defold&quot;"></a></h3>
<p>A free engine from former King developers that produces astonishingly small web builds, often under 2 MB gzipped. That makes it ideal for casual web games and mini-game platforms where load time is everything. It uses Lua and is source-available under the Defold License.</p>
<p><strong>Best for:</strong> the smallest, fastest-loading web games.</p>
<h3 id="phaser" tabindex="-1">Phaser <a class="header-anchor" href="#phaser" aria-label="Permalink to &quot;Phaser&quot;"></a></h3>
<p>The most popular HTML5 game framework, and a code-first one. Phaser 4 (released April 2026) brought a new WebGL renderer rebuilt from the ground up. It's free and open-source, you write JavaScript or TypeScript, and because it's a web framework there's no &quot;export&quot; step, your game already runs in the browser.</p>
<p><strong>Best for:</strong> developers who want a code-first, web-native 2D framework.</p>
<h3 id="gdevelop" tabindex="-1">GDevelop <a class="header-anchor" href="#gdevelop" aria-label="Permalink to &quot;GDevelop&quot;"></a></h3>
<p>Open-source and no-code, GDevelop is one of the most beginner-friendly engines around. It's free with an optional premium tier from $5 a month, and it exports unlimited HTML5 builds. A good middle ground if you want no-code but also want open-source.</p>
<p><strong>Best for:</strong> free no-code 2D.</p>
<h3 id="unity" tabindex="-1">Unity <a class="header-anchor" href="#unity" aria-label="Permalink to &quot;Unity&quot;"></a></h3>
<p>Unity's 2D toolset is mature, and the ecosystem and asset store are unmatched. It's overkill for a small 2D game, but if you want cross-platform reach or plan to grow into 3D, it earns its place. Personal is free under $200K revenue, Pro is about $2,200 a year per seat. Unlike Godot, Unity's C# does export to the web.</p>
<p><strong>Best for:</strong> teams that want the ecosystem or cross-platform reach.</p>
<h3 id="love-love2d" tabindex="-1">LÖVE (Love2D) <a class="header-anchor" href="#love-love2d" aria-label="Permalink to &quot;LÖVE (Love2D)&quot;"></a></h3>
<p>A minimalist Lua framework, not a full editor. You write code, it runs. LÖVE 11.5 is the current stable release, with 12.0 in development. Web export exists through the third-party love.js, but it isn't first-class. Reach for LÖVE if you love writing code and want almost no abstraction between you and the game loop.</p>
<p><strong>Best for:</strong> code-focused hobbyists and lightweight prototypes.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/ZiBsbBGT9L4" title="Unreal vs Unity vs Godot, which one is better in 2026?" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">InspirationTuts puts the big engines side by side for 2026.</div>
<h2 id="how-to-choose" tabindex="-1">How to Choose <a class="header-anchor" href="#how-to-choose" aria-label="Permalink to &quot;How to Choose&quot;"></a></h2>
<ul>
<li><strong>You don't want to code:</strong> Construct or GDevelop. Construct if you're shipping to web, GDevelop if you want open-source.</li>
<li><strong>You want the smallest, fastest web build:</strong> Defold.</li>
<li><strong>You write code and target the web:</strong> Phaser.</li>
<li><strong>You want one free engine for everything:</strong> Godot.</li>
<li><strong>You're shipping a commercial 2D game:</strong> GameMaker or Unity.</li>
<li><strong>You need the biggest ecosystem or plan to add 3D:</strong> Unity.</li>
</ul>
<p>Whichever you pick, you'll need art and audio. See <a href="/guides/game-assets-guide.html">where to find free game assets</a> for sources you can drop straight in.</p>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="what-is-the-best-2d-game-engine-in-2026" tabindex="-1">What is the best 2D game engine in 2026? <a class="header-anchor" href="#what-is-the-best-2d-game-engine-in-2026" aria-label="Permalink to &quot;What is the best 2D game engine in 2026?&quot;"></a></h3>
<p>For most people, Godot is the best all-round 2D engine: free, open-source, with a first-class 2D renderer. If you don't want to code, Construct (web-first) or GDevelop (open-source) are better. For commercial 2D, GameMaker and Unity are proven. The &quot;best&quot; one depends on whether you code and where you're shipping.</p>
<h3 id="which-2d-game-engine-is-best-for-beginners" tabindex="-1">Which 2D game engine is best for beginners? <a class="header-anchor" href="#which-2d-game-engine-is-best-for-beginners" aria-label="Permalink to &quot;Which 2D game engine is best for beginners?&quot;"></a></h3>
<p>Construct and GDevelop are the most beginner-friendly because they're no-code, you build with visual event systems instead of programming. GDevelop is free and open-source, Construct is subscription-based but has the smoothest web export. If you want to learn to code along the way, Godot with GDScript is the gentlest of the code-based engines.</p>
<h3 id="what-is-the-best-free-2d-game-engine" tabindex="-1">What is the best free 2D game engine? <a class="header-anchor" href="#what-is-the-best-free-2d-game-engine" aria-label="Permalink to &quot;What is the best free 2D game engine?&quot;"></a></h3>
<p>Godot is the strongest free, open-source 2D engine with no fees or royalties. GDevelop is the best free no-code option, Defold is free with the smallest web builds, and Phaser is a free open-source framework if you code. GameMaker is free for non-commercial use, then a one-time $99.99 license for commercial projects.</p>
<h3 id="which-2d-engine-makes-the-smallest-web-games" tabindex="-1">Which 2D engine makes the smallest web games? <a class="header-anchor" href="#which-2d-engine-makes-the-smallest-web-games" aria-label="Permalink to &quot;Which 2D engine makes the smallest web games?&quot;"></a></h3>
<p>Defold produces the smallest web builds, frequently under 2 MB gzipped, which is why it's popular for casual and mini-game platforms where load time decides whether players stick around. Phaser and Godot are also reasonable, but Defold leads on raw footprint.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/web-game-engines-comparison.html">Best Web Game Engines for 2026 (Compared)</a> — 2D and 3D, the full field</li>
<li><a href="/guides/godot-vs-unity-web-games.html">Godot vs Unity for Web Games</a> — the two big engines head to head</li>
<li><a href="/guides/construct-vs-gdevelop.html">Construct vs GDevelop</a> — the two no-code 2D engines compared</li>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> — art and audio for your 2D game</li>
<li><a href="/tutorials/canvas-2d-game-loop.html">Canvas 2D game loop</a> — build a 2D game with no engine at all</li>
<li><a href="/engine.html">Cinevva Engine</a> — our open-source, web-first engine</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construct vs GDevelop: Best No-Code 2D Engine (2026)]]></title>
            <link>https://app.cinevva.com/guides/construct-vs-gdevelop</link>
            <guid>https://app.cinevva.com/guides/construct-vs-gdevelop</guid>
            <pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Construct vs GDevelop in 2026 - two no-code 2D game engines compared on price, web export, 3D support, scripting, multiplayer, and open-source, with a pick for each kind of creator.]]></description>
            <content:encoded><![CDATA[<h1 id="construct-vs-gdevelop-best-no-code-2d-engine-2026" tabindex="-1">Construct vs GDevelop: Best No-Code 2D Engine (2026) <a class="header-anchor" href="#construct-vs-gdevelop-best-no-code-2d-engine-2026" aria-label="Permalink to &quot;Construct vs GDevelop: Best No-Code 2D Engine (2026)&quot;"></a></h1>
<figure style="margin:1.5rem 0">
<img src="/img/guides/construct-vs-gdevelop-hero.svg" alt="Construct vs GDevelop, no-code 2D game engines in 2026" style="width:100%;border-radius:10px">
</figure>
<p>If you want to make a game without writing code, these two lead the pack. Both build games with visual event logic instead of programming, and both export to the web. The big split is philosophy and price: Construct is a polished subscription product, GDevelop is free and open source. Here's how they compare in 2026.</p>
<p>For the wider field including code-based engines, see the <a href="/guides/web-game-engines-comparison.html">web game engines comparison</a> and <a href="/guides/best-2d-game-engines-2026.html">best 2D game engines for 2026</a>.</p>
<h2 id="quick-verdict" tabindex="-1">Quick Verdict <a class="header-anchor" href="#quick-verdict" aria-label="Permalink to &quot;Quick Verdict&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>Construct</th>
<th>GDevelop</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Model</strong></td>
<td>Proprietary, subscription</td>
<td>Free, open-source (MIT)</td>
</tr>
<tr>
<td><strong>Logic</strong></td>
<td>Event sheets</td>
<td>Event system</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>Free tier; Personal ~$129.99/yr</td>
<td>Free; premium from ~$5.49/mo</td>
</tr>
<tr>
<td><strong>Web export</strong></td>
<td>Excellent (HTML5-native)</td>
<td>Yes (HTML5)</td>
</tr>
<tr>
<td><strong>3D</strong></td>
<td>Limited (3D camera + 3D objects)</td>
<td>Full real-time 3D editor (5.6)</td>
</tr>
<tr>
<td><strong>Scripting</strong></td>
<td>JavaScript and TypeScript</td>
<td>Events + JavaScript</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>Polished workflow, built-in multiplayer</td>
<td>Free, open-source, newer 3D</td>
</tr>
</tbody>
</table>
<h2 id="no-code-two-ways" tabindex="-1">No-Code, Two Ways <a class="header-anchor" href="#no-code-two-ways" aria-label="Permalink to &quot;No-Code, Two Ways&quot;"></a></h2>
<p>Both engines let you build a game by stacking visual conditions and actions, no programming required. Construct uses &quot;event sheets,&quot; a clean, spreadsheet-like flow that's widely praised for how fast you can prototype in it. GDevelop uses a similar event system and is just as approachable for beginners.</p>
<p>The difference that matters most: <strong>Construct is a paid, closed-source product; GDevelop is free and open source under the MIT license.</strong> If open source or zero cost is a priority, that alone may decide it.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/MDlP01bwczo" title="Why I Switched From Construct 3 To GDevelop" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">One creator's take on moving from Construct to GDevelop.</div>
<h2 id="pricing" tabindex="-1">Pricing <a class="header-anchor" href="#pricing" aria-label="Permalink to &quot;Pricing&quot;"></a></h2>
<p>Construct 3 runs on a subscription. There's a limited free edition, and the Personal plan is around $15.99 a month or $129.99 a year, with Education and Business tiers above that. You're paying for a polished, actively developed product.</p>
<p>GDevelop's engine is free and open source. Optional premium tiers (around $5.49 Silver, $10.99 Gold, and $32.99 Pro per month) add cloud builds and online services, but you can build and export games fully for free. Prices rose modestly in early 2026.</p>
<p>For a hobbyist or anyone cost-sensitive, GDevelop's free tier is hard to argue with. For a studio that wants a refined commercial tool, Construct's subscription is reasonable.</p>
<h2 id="_3d-scripting-and-multiplayer" tabindex="-1">3D, Scripting, and Multiplayer <a class="header-anchor" href="#_3d-scripting-and-multiplayer" aria-label="Permalink to &quot;3D, Scripting, and Multiplayer&quot;"></a></h2>
<p><strong>3D:</strong> GDevelop pulled ahead here. Its 5.6 release (December 2025) shipped a full real-time 3D editor with Jolt-based physics, shadows, animated models, 3D particles, and ready-made character and vehicle behaviors. Construct added 3D too, but it's more limited, a 3D camera plus 3D object plugins that load glTF models, layered on a fundamentally 2D engine.</p>
<p><strong>Scripting:</strong> when you outgrow events, both let you drop into code. Construct supports JavaScript and TypeScript alongside event sheets. GDevelop supports JavaScript through its events and extensions. Both keep the no-code path primary.</p>
<p><strong>Multiplayer:</strong> Construct ships a built-in Multiplayer object over WebRTC data channels for peer-to-peer games. GDevelop offers multiplayer through its online services and extensions.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/Jjsfk-vzlWQ" title="Construct 3 Is Officially Going 3D" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">FoozleCC on Construct's move into 3D.</div>
<h2 id="when-to-pick-which" tabindex="-1">When to Pick Which <a class="header-anchor" href="#when-to-pick-which" aria-label="Permalink to &quot;When to Pick Which&quot;"></a></h2>
<p>Pick <strong>GDevelop</strong> if you want free and open source, cross-platform export, and the more capable real-time 3D editor. It's the better fit for hobbyists, students, and anyone who wants to own their tooling.</p>
<p>Pick <strong>Construct</strong> if you want the most polished event-sheet workflow, mature and reliable HTML5 export, and built-in WebRTC multiplayer, and you don't mind a subscription for a refined commercial product.</p>
<p>Either way, you'll need art and audio. See <a href="/guides/game-assets-guide.html">where to find free game assets</a> for sources you can use right away.</p>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="is-construct-or-gdevelop-better-for-beginners" tabindex="-1">Is Construct or GDevelop better for beginners? <a class="header-anchor" href="#is-construct-or-gdevelop-better-for-beginners" aria-label="Permalink to &quot;Is Construct or GDevelop better for beginners?&quot;"></a></h3>
<p>Both are designed for beginners and use visual event logic with no coding required. GDevelop wins on cost (it's free and open source) and now has a stronger 3D editor. Construct wins on workflow polish and built-in multiplayer. If budget matters, start with GDevelop. If you want the most refined 2D event workflow, try Construct's free edition first.</p>
<h3 id="is-gdevelop-really-free" tabindex="-1">Is GDevelop really free? <a class="header-anchor" href="#is-gdevelop-really-free" aria-label="Permalink to &quot;Is GDevelop really free?&quot;"></a></h3>
<p>Yes. The GDevelop engine is free and open source under the MIT license, and you can build and export HTML5 games at no cost. Optional premium tiers (from about $5.49/month) add cloud builds and online services, but they're not required to make and ship a game.</p>
<h3 id="can-construct-and-gdevelop-make-3d-games" tabindex="-1">Can Construct and GDevelop make 3D games? <a class="header-anchor" href="#can-construct-and-gdevelop-make-3d-games" aria-label="Permalink to &quot;Can Construct and GDevelop make 3D games?&quot;"></a></h3>
<p>Both have 3D, but GDevelop is further along. GDevelop 5.6 (December 2025) added a full real-time 3D editor with physics, shadows, and animated models. Construct offers a 3D camera and 3D object plugins that load glTF models, but it remains primarily a 2D engine. For 3D-leaning projects, GDevelop is the stronger no-code option.</p>
<h3 id="do-construct-and-gdevelop-export-to-the-web" tabindex="-1">Do Construct and GDevelop export to the web? <a class="header-anchor" href="#do-construct-and-gdevelop-export-to-the-web" aria-label="Permalink to &quot;Do Construct and GDevelop export to the web?&quot;"></a></h3>
<p>Yes, both export HTML5 games that run in the browser. Construct is HTML5-native with mature, reliable web export. GDevelop exports unlimited HTML5 builds, including locally for free. Both are solid choices for browser games.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/best-2d-game-engines-2026.html">Best 2D Game Engines for 2026</a> — the full 2D field, code and no-code</li>
<li><a href="/guides/web-game-engines-comparison.html">Best Web Game Engines for 2026 (Compared)</a> — 2D and 3D</li>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> — art and audio for your game</li>
<li><a href="/guides/itch-io-launch-guide.html">How to Launch Your Game on itch.io</a> — getting your game in front of players</li>
<li><a href="/engine.html">Cinevva</a> — make games with AI-powered tools, web-first</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Godot vs Unity for Web Games (2026)]]></title>
            <link>https://app.cinevva.com/guides/godot-vs-unity-web-games</link>
            <guid>https://app.cinevva.com/guides/godot-vs-unity-web-games</guid>
            <pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Godot vs Unity for browser games in 2026 - build size, WebGL2 vs WebGPU, the C# web-export limit, iOS support, and pricing, with a clear pick for each kind of web game.]]></description>
            <content:encoded><![CDATA[<h1 id="godot-vs-unity-for-web-games-2026" tabindex="-1">Godot vs Unity for Web Games (2026) <a class="header-anchor" href="#godot-vs-unity-for-web-games-2026" aria-label="Permalink to &quot;Godot vs Unity for Web Games (2026)&quot;"></a></h1>
<figure style="margin:1.5rem 0">
<img src="/img/guides/godot-vs-unity-web-hero.svg" alt="Godot vs Unity for web games in 2026" style="width:100%;border-radius:10px">
</figure>
<p>Both Godot and Unity can ship a game to the browser. They get there very differently, and for web specifically the gap between them is sharper than the general &quot;Godot vs Unity&quot; debate suggests. This guide compares them on the things that actually decide a web build: download size, rendering, language support, mobile browsers, and cost.</p>
<p>If you want the wider picture across every browser engine, start with our <a href="/guides/web-game-engines-comparison.html">web game engines comparison</a>. This page zooms in on the two big full-engine options.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/84Prr0SOMvk" title="Unity or Godot in 2025 - Which Game Engine is Right for You?" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Gamefromscratch walks through where each engine fits. The short version for web is below.</div>
<h2 id="quick-verdict" tabindex="-1">Quick Verdict <a class="header-anchor" href="#quick-verdict" aria-label="Permalink to &quot;Quick Verdict&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>Godot</th>
<th>Unity</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Web build size (empty)</strong></td>
<td>~5 MB Brotli (~40 MB uncompressed)</td>
<td>~7.7 MB 2D, ~10.7 MB 3D, ~2 MB stripped (Brotli)</td>
</tr>
<tr>
<td><strong>Web language</strong></td>
<td>GDScript only (C# can't export to web yet)</td>
<td>C# (works on web)</td>
</tr>
<tr>
<td><strong>Web rendering</strong></td>
<td>WebGL2 (Compatibility renderer)</td>
<td>WebGL2, plus experimental WebGPU</td>
</tr>
<tr>
<td><strong>iOS/Safari</strong></td>
<td>Good with single-threaded export (4.3+)</td>
<td>Mobile web from Unity 6</td>
</tr>
<tr>
<td><strong>Cost</strong></td>
<td>Free, MIT, no fees ever</td>
<td>Personal free under $200K; Pro ~$2,200/yr/seat</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>Free, small 2D and lighter 3D web games</td>
<td>Heavier 3D, C# teams, big asset ecosystem</td>
</tr>
</tbody>
</table>
<p>The single biggest deciding factor: <strong>if your game is written in C#, Unity can ship it to the web and Godot still cannot.</strong> Everything else flows from there.</p>
<h2 id="build-size-and-load-time" tabindex="-1">Build Size and Load Time <a class="header-anchor" href="#build-size-and-load-time" aria-label="Permalink to &quot;Build Size and Load Time&quot;"></a></h2>
<p>Download size is the first thing a web player feels. Godot's web export ships the engine as a WebAssembly binary that's around 40 MB uncompressed but compresses to roughly 5 MB with Brotli. The size comes from the prebuilt template carrying the full engine (3D renderer, audio, networking) because it can't know which features your game uses.</p>
<p>Unity's empty web builds, measured on Unity 6 with Brotli, land around 7.7 MB for a 2D built-in pipeline project and 10.7 MB for a 3D URP template. An aggressively stripped minimal build can get down near 2 MB. So a carefully trimmed Unity 2D build can approach Godot's footprint, but Godot's default is smaller with less effort.</p>
<p>For both engines, your assets usually dwarf the engine binary anyway. Compress textures and audio, stream what you can, and the engine choice matters less than your asset budget.</p>
<h2 id="the-c-limitation-read-this-before-you-choose" tabindex="-1">The C# Limitation (Read This Before You Choose) <a class="header-anchor" href="#the-c-limitation-read-this-before-you-choose" aria-label="Permalink to &quot;The C# Limitation (Read This Before You Choose)&quot;"></a></h2>
<p>This is the one that catches teams out. As of 2026, <strong>Godot projects written in C# (.NET) cannot officially export to the web.</strong> GDScript exports fine. A working C# web prototype was demonstrated at GodotCon Boston in May 2025 by statically linking Mono, but it shipped a large payload and the team would not commit to a release timeline.</p>
<p>Unity, by contrast, compiles C# to WebAssembly and has shipped C# to the browser for years. So if you have an existing C# codebase, or your team only knows C#, Unity is the practical choice for web. If you're starting fresh and happy in GDScript, Godot's web path is clean.</p>
<h2 id="rendering-webgl2-vs-webgpu" tabindex="-1">Rendering: WebGL2 vs WebGPU <a class="header-anchor" href="#rendering-webgl2-vs-webgpu" aria-label="Permalink to &quot;Rendering: WebGL2 vs WebGPU&quot;"></a></h2>
<p>Godot's web export targets WebGL2 through its Compatibility renderer. Its broader WebGPU work isn't production-ready for web export yet. Godot 4.5 (September 2025) did add WebAssembly SIMD, which helps web performance without code changes.</p>
<p>Unity 6 added an experimental WebGPU backend that exposes compute shaders and modern GPU features beyond WebGL2, with WebGL2 still the safe fallback. WebGPU isn't available on every browser yet (see our <a href="/guides/web-games-stack-2026.html">web games tech stack guide</a> for current support), so treat it as an upgrade path, not a baseline. Unity 6 also raised the WebAssembly heap to 4 GB and added Wasm SIMD and native exceptions.</p>
<p>If you need compute shaders or heavier 3D in the browser today, Unity's WebGPU path is ahead. For 2D and lighter 3D on WebGL2, both are fine.</p>
<h2 id="threading-ios-and-hosting-headaches" tabindex="-1">Threading, iOS, and Hosting Headaches <a class="header-anchor" href="#threading-ios-and-hosting-headaches" aria-label="Permalink to &quot;Threading, iOS, and Hosting Headaches&quot;"></a></h2>
<p>Godot 4 originally required SharedArrayBuffer, which browsers only expose when the page is cross-origin isolated (the COOP and COEP headers). That's a hosting headache, and on itch.io it only works in Chrome. Since Godot 4.3 you can export single-threaded builds that drop the requirement entirely, and that change also fixed the long-standing iOS and Safari playback problems. For a web-first Godot game, single-threaded export is usually the safer default.</p>
<p>Unity's web builds use WebAssembly too, with mobile web support starting at Unity 6. If you're targeting broad mobile browsers, test on real devices early in both engines, because that's where web builds break first.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/WB7v-ezqcJM" title="I Made The Same Game in Unity and Godot, Which Engine is Better?" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Thomas Grové builds the same game in both engines, a useful gut-check on day-to-day workflow.</div>
<h2 id="pricing" tabindex="-1">Pricing <a class="header-anchor" href="#pricing" aria-label="Permalink to &quot;Pricing&quot;"></a></h2>
<p>Godot is free under the MIT license. No seats, no royalties, no revenue cap, no fees, ever.</p>
<p>Unity reverted to seat-based subscriptions after canceling the Runtime Fee in September 2024 (it hasn't returned). Unity Personal stays free up to $200K in revenue or funding, and the &quot;Made with Unity&quot; splash became optional on Unity 6. Unity Pro is about $2,200 per seat per year, with a roughly 5% increase to paid tiers in early 2026. For a solo dev or small team under the cap, both engines are effectively free to ship with.</p>
<h2 id="when-to-pick-which" tabindex="-1">When to Pick Which <a class="header-anchor" href="#when-to-pick-which" aria-label="Permalink to &quot;When to Pick Which&quot;"></a></h2>
<p>Pick <strong>Godot</strong> for web when you want a free, small-footprint 2D or lighter-3D browser game, smooth iOS and Safari support through single-threaded export, and you're comfortable writing GDScript.</p>
<p>Pick <strong>Unity</strong> for web when you need heavier 3D or compute shaders via WebGPU, you want the larger asset ecosystem, or you're a C# team, and you can accept larger builds and the experimental-WebGPU caveat.</p>
<p>Either way, your build still has to load fast and run on a phone. See <a href="/tutorials/ship-web-game-fast.html">ship a web game that loads fast</a> once you've picked.</p>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="can-godot-export-c-games-to-the-web" tabindex="-1">Can Godot export C# games to the web? <a class="header-anchor" href="#can-godot-export-c-games-to-the-web" aria-label="Permalink to &quot;Can Godot export C# games to the web?&quot;"></a></h3>
<p>Not officially, as of 2026. Godot's web export works for GDScript, but C# (.NET) projects cannot export to the browser yet. A prototype was shown at GodotCon Boston in May 2025, but there's no committed release date. If you need C# on the web, Unity is currently the only one of the two that ships it.</p>
<h3 id="is-godot-or-unity-smaller-for-web-builds" tabindex="-1">Is Godot or Unity smaller for web builds? <a class="header-anchor" href="#is-godot-or-unity-smaller-for-web-builds" aria-label="Permalink to &quot;Is Godot or Unity smaller for web builds?&quot;"></a></h3>
<p>Godot's empty web build is around 5 MB compressed with Brotli. Unity 6 empty builds run about 7.7 MB for 2D and 10.7 MB for 3D, though an aggressively stripped Unity build can reach roughly 2 MB. Godot is smaller by default, but a tuned Unity 2D build can get close. Your own assets usually matter more than the engine binary.</p>
<h3 id="does-unity-support-webgpu-in-the-browser" tabindex="-1">Does Unity support WebGPU in the browser? <a class="header-anchor" href="#does-unity-support-webgpu-in-the-browser" aria-label="Permalink to &quot;Does Unity support WebGPU in the browser?&quot;"></a></h3>
<p>Yes, Unity 6 added an experimental WebGPU backend with compute shaders and modern GPU features, with WebGL2 as the fallback. It's labeled experimental and isn't supported on every browser, so ship WebGL2 as your baseline and treat WebGPU as an enhancement.</p>
<h3 id="which-is-better-for-browser-games-godot-or-unity" tabindex="-1">Which is better for browser games, Godot or Unity? <a class="header-anchor" href="#which-is-better-for-browser-games-godot-or-unity" aria-label="Permalink to &quot;Which is better for browser games, Godot or Unity?&quot;"></a></h3>
<p>For free, lightweight 2D or lighter 3D games with good iOS support, Godot is the cleaner web path, as long as you use GDScript. For heavier 3D, compute shaders, the bigger ecosystem, or an existing C# codebase, Unity wins on web. The C# question usually decides it.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/web-game-engines-comparison.html">Best Web Game Engines for 2026 (Compared)</a> — the full field, beyond these two</li>
<li><a href="/guides/unity-alternatives-web-games.html">Unity Alternatives for Web Games</a> — every web-capable option, picked by why you're leaving Unity</li>
<li><a href="/guides/best-2d-game-engines-2026.html">Best 2D Game Engines for 2026</a> — if your game is 2D, start here</li>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> — WebGL, WebGPU, and Wasm explained</li>
<li><a href="/tutorials/ship-web-game-fast.html">Ship a web game that loads fast</a> — build-size and load-time tactics</li>
<li><a href="/engine.html">Cinevva Engine</a> — our open-source, web-first engine built on Three.js</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PlayCanvas vs Three.js: Web 3D Compared (2026)]]></title>
            <link>https://app.cinevva.com/guides/playcanvas-vs-threejs</link>
            <guid>https://app.cinevva.com/guides/playcanvas-vs-threejs</guid>
            <pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[PlayCanvas vs Three.js for 3D on the web - full engine with a visual editor versus a rendering library, plus WebGPU support, built-in systems, pricing, and when to pick each.]]></description>
            <content:encoded><![CDATA[<h1 id="playcanvas-vs-three-js-web-3d-compared-2026" tabindex="-1">PlayCanvas vs Three.js: Web 3D Compared (2026) <a class="header-anchor" href="#playcanvas-vs-three-js-web-3d-compared-2026" aria-label="Permalink to &quot;PlayCanvas vs Three.js: Web 3D Compared (2026)&quot;"></a></h1>
<figure style="margin:1.5rem 0">
<img src="/img/guides/playcanvas-vs-threejs-hero.svg" alt="PlayCanvas vs Three.js for web 3D in 2026" style="width:100%;border-radius:10px">
</figure>
<p>People line these two up as rivals, but they're not the same kind of tool. PlayCanvas is a full game engine with a visual editor. Three.js is a rendering library you build on with code. Picking between them is really a question of how much you want handed to you versus how much you want to control. Here's the breakdown for web 3D in 2026.</p>
<p>For the wider field, see our <a href="/guides/web-game-engines-comparison.html">web game engines comparison</a>.</p>
<h2 id="quick-verdict" tabindex="-1">Quick Verdict <a class="header-anchor" href="#quick-verdict" aria-label="Permalink to &quot;Quick Verdict&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>PlayCanvas</th>
<th>Three.js</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>What it is</strong></td>
<td>Full engine + cloud editor</td>
<td>Rendering library (no editor)</td>
</tr>
<tr>
<td><strong>Workflow</strong></td>
<td>Visual editor, real-time collaboration</td>
<td>Code-only</td>
</tr>
<tr>
<td><strong>Rendering</strong></td>
<td>WebGL2 + WebGPU</td>
<td>WebGL2 + WebGPU (WebGPURenderer)</td>
</tr>
<tr>
<td><strong>Built-in systems</strong></td>
<td>ECS, physics, animation, audio, input</td>
<td>Renderer only, you add the rest</td>
</tr>
<tr>
<td><strong>License/cost</strong></td>
<td>Engine MIT; editor free to $50/seat/mo</td>
<td>MIT, free</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>Teams wanting a turnkey engine + editor</td>
<td>Custom, code-first, maximum control</td>
</tr>
</tbody>
</table>
<h2 id="engine-vs-library" tabindex="-1">Engine vs Library <a class="header-anchor" href="#engine-vs-library" aria-label="Permalink to &quot;Engine vs Library&quot;"></a></h2>
<p>This is the whole story in one line. <strong>PlayCanvas gives you a game engine</strong>: an entity-component system, physics, animation, audio, input, and an asset pipeline, all wired together, plus a browser-based editor where a team can build a scene together in real time (think collaborative editing, like a design tool for 3D).</p>
<p><strong>Three.js gives you a renderer.</strong> Scene graph, cameras, lights, materials, and a fast WebGL/WebGPU renderer. No editor, no physics, no game loop opinions. You add physics with something like Rapier or cannon-es, structure your own game logic, and build whatever tooling you need. That's freedom and also more work.</p>
<p>So the real question isn't &quot;which is better,&quot; it's &quot;do I want an engine or a renderer?&quot;</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/-0envvPmj5k" title="PlayCanvas Game Engine Open Sourced" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Gamefromscratch on PlayCanvas going open source.</div>
<h2 id="rendering-and-webgpu" tabindex="-1">Rendering and WebGPU <a class="header-anchor" href="#rendering-and-webgpu" aria-label="Permalink to &quot;Rendering and WebGPU&quot;"></a></h2>
<p>Both are modern on the GPU front. PlayCanvas runs a dual WebGL2 and WebGPU backend. Its v2.0 release (August 2024) dropped WebGL1 entirely, and recent engine builds added a compute-based WebGPU path for 3D Gaussian splats. Three.js shipped a production-ready WebGPURenderer around r171 (September 2025) with near zero-config setup and an automatic WebGL2 fallback, plus TSL (Three.js Shading Language) so you can author shaders once and target both backends.</p>
<p>For most projects in 2026 either will render beautifully. WebGPU is the upgrade path on both, with WebGL2 as the safe baseline. See our <a href="/guides/web-games-stack-2026.html">web games tech stack guide</a> for current browser support.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/7we9mqIOKyw" title="Embracing WebGPU and WebXR With Three.js - Mr.doob, JSNation 2024" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Three.js creator Mr.doob on where WebGPU and WebXR are taking the library.</div>
<h2 id="workflow-licensing-and-cost" tabindex="-1">Workflow, Licensing, and Cost <a class="header-anchor" href="#workflow-licensing-and-cost" aria-label="Permalink to &quot;Workflow, Licensing, and Cost&quot;"></a></h2>
<p>PlayCanvas's editor is its headline feature: a hosted, collaborative scene editor with an asset pipeline. The engine is open source under MIT, and the editor frontend was open-sourced in 2025 too. The hosted editor has tiers, a free plan, a Personal plan around $15 a month, and an Organization plan around $50 per seat a month, mostly differing on storage and private project limits.</p>
<p>Three.js is MIT and free, full stop. There's no editor to pay for because there's no editor. Your &quot;cost&quot; is the engineering time to build the structure PlayCanvas hands you.</p>
<h2 id="when-to-pick-which" tabindex="-1">When to Pick Which <a class="header-anchor" href="#when-to-pick-which" aria-label="Permalink to &quot;When to Pick Which&quot;"></a></h2>
<p>Pick <strong>PlayCanvas</strong> when you want a complete engine and a visual editor out of the box, your team needs to collaborate on scenes, and you'd rather configure than build from scratch. It's especially strong for product configurators, interactive 3D, and teams shipping quickly.</p>
<p>Pick <strong>Three.js</strong> when you want full control over rendering and architecture, you're comfortable assembling your own stack, or you're building something bespoke where an engine's opinions would get in the way. It's also the larger ecosystem and the most in-demand web-3D skill. We built <a href="/engine.html">Cinevva Engine</a> on Three.js for exactly that reason: web-first, open, and no ceiling on what we can customize.</p>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="is-playcanvas-or-three-js-better-for-web-games" tabindex="-1">Is PlayCanvas or Three.js better for web games? <a class="header-anchor" href="#is-playcanvas-or-three-js-better-for-web-games" aria-label="Permalink to &quot;Is PlayCanvas or Three.js better for web games?&quot;"></a></h3>
<p>Neither is strictly better, they're different tools. PlayCanvas is a full engine with a visual editor, so it's faster to ship a structured game or interactive app, especially for a team. Three.js is a rendering library that gives you total control but expects you to build the game systems yourself. Choose PlayCanvas for turnkey, Three.js for custom.</p>
<h3 id="is-three-js-a-game-engine" tabindex="-1">Is Three.js a game engine? <a class="header-anchor" href="#is-three-js-a-game-engine" aria-label="Permalink to &quot;Is Three.js a game engine?&quot;"></a></h3>
<p>No. Three.js is a 3D rendering library. It handles the scene graph, cameras, lights, materials, and rendering through WebGL2 or WebGPU, but it has no built-in physics, audio, input, or game loop. You add those with other libraries (like Rapier for physics) or build them yourself. PlayCanvas, by contrast, is a full engine.</p>
<h3 id="do-playcanvas-and-three-js-support-webgpu" tabindex="-1">Do PlayCanvas and Three.js support WebGPU? <a class="header-anchor" href="#do-playcanvas-and-three-js-support-webgpu" aria-label="Permalink to &quot;Do PlayCanvas and Three.js support WebGPU?&quot;"></a></h3>
<p>Yes, both do in 2026. PlayCanvas runs a dual WebGL2/WebGPU backend. Three.js has a production-ready WebGPURenderer (stable since around r171) with an automatic WebGL2 fallback and TSL for cross-target shaders. Ship WebGL2 as your baseline and treat WebGPU as the performance upgrade.</p>
<h3 id="is-playcanvas-free" tabindex="-1">Is PlayCanvas free? <a class="header-anchor" href="#is-playcanvas-free" aria-label="Permalink to &quot;Is PlayCanvas free?&quot;"></a></h3>
<p>The PlayCanvas engine is free and open source under the MIT license, and the editor frontend was open-sourced in 2025. The hosted cloud editor has a free tier plus paid plans (around $15/month Personal and $50/seat/month Organization) that mainly add storage and private projects. Three.js is entirely free with no editor to pay for.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/web-game-engines-comparison.html">Best Web Game Engines for 2026 (Compared)</a> — the full field</li>
<li><a href="/guides/threejs-vs-babylonjs.html">Three.js vs Babylon.js</a> — if you've narrowed it to web-3D libraries</li>
<li><a href="/guides/godot-vs-unity-web-games.html">Godot vs Unity for Web Games</a> — full engines instead of libraries</li>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> — WebGL, WebGPU, and Wasm</li>
<li><a href="/engine.html">Cinevva Engine</a> — our open-source engine built on Three.js</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Three.js vs Babylon.js: Which Web 3D Library (2026)]]></title>
            <link>https://app.cinevva.com/guides/threejs-vs-babylonjs</link>
            <guid>https://app.cinevva.com/guides/threejs-vs-babylonjs</guid>
            <pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Three.js vs Babylon.js in 2026 - minimal renderer versus full engine, WebGPU and WGSL support, built-in tools, bundle size, TypeScript, and when to pick each for web 3D.]]></description>
            <content:encoded><![CDATA[<h1 id="three-js-vs-babylon-js-which-web-3d-library-2026" tabindex="-1">Three.js vs Babylon.js: Which Web 3D Library (2026) <a class="header-anchor" href="#three-js-vs-babylon-js-which-web-3d-library-2026" aria-label="Permalink to &quot;Three.js vs Babylon.js: Which Web 3D Library (2026)&quot;"></a></h1>
<figure style="margin:1.5rem 0">
<img src="/img/guides/threejs-vs-babylonjs-hero.svg" alt="Three.js vs Babylon.js for web 3D in 2026" style="width:100%;border-radius:10px">
</figure>
<p>These are the two heavyweights of web 3D, and the choice between them comes down to philosophy. Three.js is a lean renderer you extend. Babylon.js is a full engine with the tools already in the box. Both are excellent, both are free, both do WebGPU in 2026. Here's how to pick.</p>
<p>For 2D engines or full game engines instead, see the <a href="/guides/web-game-engines-comparison.html">web game engines comparison</a>.</p>
<h2 id="quick-verdict" tabindex="-1">Quick Verdict <a class="header-anchor" href="#quick-verdict" aria-label="Permalink to &quot;Quick Verdict&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th>Three.js</th>
<th>Babylon.js</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Style</strong></td>
<td>Minimal renderer/library</td>
<td>Full engine/framework</td>
</tr>
<tr>
<td><strong>Language</strong></td>
<td>JavaScript (with type defs)</td>
<td>TypeScript-first</td>
</tr>
<tr>
<td><strong>Built-in tools</strong></td>
<td>Renderer; add-ons for the rest</td>
<td>Physics, GUI, Inspector, Playground, NME</td>
</tr>
<tr>
<td><strong>WebGPU</strong></td>
<td>WebGPURenderer + TSL</td>
<td>GLSL + WGSL core (8.0)</td>
</tr>
<tr>
<td><strong>Bundle</strong></td>
<td>Smaller, tree-shakeable core</td>
<td>Larger but modular</td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>MIT</td>
<td>Apache 2.0</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>Custom, lightweight, biggest ecosystem</td>
<td>Batteries-included with strong tooling</td>
</tr>
</tbody>
</table>
<h2 id="library-vs-framework" tabindex="-1">Library vs Framework <a class="header-anchor" href="#library-vs-framework" aria-label="Permalink to &quot;Library vs Framework&quot;"></a></h2>
<p>Three.js keeps a small core and leaves the rest to you and the community. You get a great renderer, scene graph, and materials, then reach for add-ons when you need physics, post-processing, or controls. It's the most popular web-3D library and has the largest ecosystem, which means more examples, more tutorials, and more hiring pool.</p>
<p>Babylon.js is a fuller engine, backed by Microsoft. It ships physics, a GUI system, animation, a built-in Inspector for debugging scenes, an online Playground for sharing code, and a Node Material Editor for building shaders visually. If you want tooling and structure provided rather than assembled, Babylon hands you more on day one.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/kKaomUggipQ" title="Introducing Babylon.js 8.0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">The official Babylon.js 8.0 showcase gives a sense of how much ships in the box.</div>
<h2 id="webgpu-in-2026" tabindex="-1">WebGPU in 2026 <a class="header-anchor" href="#webgpu-in-2026" aria-label="Permalink to &quot;WebGPU in 2026&quot;"></a></h2>
<p>Both are serious about WebGPU now. Babylon.js 8.0 (March 2025) made all core engine shaders ship in both GLSL and WGSL, which removed a multi-megabyte conversion layer and made the engine roughly half the size when targeting WebGPU. Babylon.js 9.0 (March 2026) pushed further with clustered lighting, volumetric lighting via compute shaders, a frame graph, and area lights.</p>
<p>Three.js has its production-ready WebGPURenderer with automatic WebGL2 fallback, and TSL lets you write shaders once for both backends. Recent releases keep refining it.</p>
<p>Net: WebGPU is solid in both. Babylon leans into compute-shader-driven features; Three.js keeps its renderer lean and flexible.</p>
<h2 id="typescript-bundle-size-and-ecosystem" tabindex="-1">TypeScript, Bundle Size, and Ecosystem <a class="header-anchor" href="#typescript-bundle-size-and-ecosystem" aria-label="Permalink to &quot;TypeScript, Bundle Size, and Ecosystem&quot;"></a></h2>
<p>Babylon.js is TypeScript-first, written almost entirely in TS, which many teams prefer for large codebases. Three.js is JavaScript with separate type definitions that work well but aren't the source of truth.</p>
<p>On size, Three.js has the smaller, more tree-shakeable core, so a minimal scene ships less code. Babylon is larger but modular through scoped packages, and its WGSL-native path narrowed the WebGPU gap.</p>
<p>Ecosystem goes to Three.js on raw size and community output. Tooling-in-the-box goes to Babylon.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/6k5ZK3SV3ko" title="Moving Three.js from WebGL to WebGPU - Mr.doob, JSConf JP 2024" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Three.js creator Mr.doob on the move to WebGPU.</div>
<h2 id="when-to-pick-which" tabindex="-1">When to Pick Which <a class="header-anchor" href="#when-to-pick-which" aria-label="Permalink to &quot;When to Pick Which&quot;"></a></h2>
<p>Pick <strong>Three.js</strong> when you want a lightweight, flexible renderer, the biggest ecosystem, and freedom to architect things your way. It's the default for custom web-3D work and the most transferable skill.</p>
<p>Pick <strong>Babylon.js</strong> when you want a full engine with strong built-in tooling (Inspector, Playground, Node Material Editor), TypeScript-first development, and corporate-backed stability, and you're happy trading a bit of bundle size for batteries included.</p>
<p>Both are great picks. We chose Three.js for <a href="/engine.html">Cinevva Engine</a> because we wanted a lean, web-first foundation we could shape ourselves.</p>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="is-three-js-or-babylon-js-better" tabindex="-1">Is Three.js or Babylon.js better? <a class="header-anchor" href="#is-three-js-or-babylon-js-better" aria-label="Permalink to &quot;Is Three.js or Babylon.js better?&quot;"></a></h3>
<p>Both are excellent and free. Three.js is a minimal renderer with the largest ecosystem, best when you want flexibility and to build your own structure. Babylon.js is a fuller engine with built-in physics, GUI, an Inspector, and a Playground, best when you want tooling provided and TypeScript-first development. It's a philosophy choice, not a quality gap.</p>
<h3 id="do-three-js-and-babylon-js-support-webgpu" tabindex="-1">Do Three.js and Babylon.js support WebGPU? <a class="header-anchor" href="#do-three-js-and-babylon-js-support-webgpu" aria-label="Permalink to &quot;Do Three.js and Babylon.js support WebGPU?&quot;"></a></h3>
<p>Yes, both support WebGPU in 2026. Three.js has a production-ready WebGPURenderer with WebGL2 fallback and TSL for cross-target shaders. Babylon.js 8.0 made its core shaders WGSL-native (cutting bundle size on WebGPU), and 9.0 added compute-shader features like volumetric lighting. Ship WebGL2 as the baseline and use WebGPU where it's available.</p>
<h3 id="is-babylon-js-a-game-engine-and-three-js-not" tabindex="-1">Is Babylon.js a game engine and Three.js not? <a class="header-anchor" href="#is-babylon-js-a-game-engine-and-three-js-not" aria-label="Permalink to &quot;Is Babylon.js a game engine and Three.js not?&quot;"></a></h3>
<p>Roughly, yes. Babylon.js is a full engine: it includes physics, a GUI system, animation, an Inspector, and editor-style tooling. Three.js is a rendering library focused on drawing 3D, so you add physics, controls, and game systems through other packages or your own code. That's the core difference between them.</p>
<h3 id="which-is-smaller-three-js-or-babylon-js" tabindex="-1">Which is smaller, Three.js or Babylon.js? <a class="header-anchor" href="#which-is-smaller-three-js-or-babylon-js" aria-label="Permalink to &quot;Which is smaller, Three.js or Babylon.js?&quot;"></a></h3>
<p>Three.js has the smaller, more tree-shakeable core, so a minimal scene ships less JavaScript. Babylon.js is larger overall but modular through scoped packages, and its WGSL-native shaders in 8.0 roughly halved its size when targeting WebGPU. For the tightest bundle, Three.js still wins.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/playcanvas-vs-threejs.html">PlayCanvas vs Three.js</a> — add a visual editor to the comparison</li>
<li><a href="/guides/web-game-engines-comparison.html">Best Web Game Engines for 2026 (Compared)</a> — the full field</li>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> — WebGL, WebGPU, and Wasm</li>
<li><a href="/guides/threejs-usdc-tech-report.html">Three.js + USDC in the Browser</a> — loading USD 3D assets in Three.js</li>
<li><a href="/engine.html">Cinevva Engine</a> — our open-source engine built on Three.js</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Giving Players the Power to Sculpt the World]]></title>
            <link>https://app.cinevva.com/guides/player-world-sculpting</link>
            <guid>https://app.cinevva.com/guides/player-world-sculpting</guid>
            <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How to turn terrain brushes into toys. TNT that carves caves, seeds that grow forests, rain that erodes cliffs. Game design patterns for making world sculpting fun for casual players in a shared multiplayer world.]]></description>
            <content:encoded><![CDATA[<h1 id="giving-players-the-power-to-sculpt-the-world" tabindex="-1">Giving Players the Power to Sculpt the World <a class="header-anchor" href="#giving-players-the-power-to-sculpt-the-world" aria-label="Permalink to &quot;Giving Players the Power to Sculpt the World&quot;"></a></h1>
<img src="https://cdn.cinevva.com/guides/player-world-sculpting-hero.jpg" alt="Player surrounded by terrain-sculpting tools: dynamite, hoe, watering can, seeds, pickaxe, rain cloud, and a terraform beacon, reshaping a colorful 3D landscape" style="width:100%;border-radius:8px;margin:1.5rem 0" />
<p>Nobody opens a game and thinks &quot;I want to apply a smooth falloff function with Hermite interpolation to a signed distance field.&quot; They think &quot;I want to dig a cave&quot; or &quot;I want to build a mountain&quot; or &quot;I want to make a river.&quot;</p>
<p>The technical side of terrain sculpting (marching cubes, SDF brushes, transvoxel seams) is covered in our <a href="/guides/terrain-sculpting-brushes.html">companion research guide</a>. This guide is about the other half of the problem: how to put sculpting power into casual players' hands and make it feel like play, not like a 3D modeling tool.</p>
<h2 id="the-metaphor-problem" tabindex="-1">The Metaphor Problem <a class="header-anchor" href="#the-metaphor-problem" aria-label="Permalink to &quot;The Metaphor Problem&quot;"></a></h2>
<p>Every shipped game that lets players reshape terrain has solved the same design problem: raw sculpting parameters (radius, strength, falloff, operation type) are meaningless to most players. The solution is always the same: wrap the brush in a metaphor the player already understands.</p>
<h3 id="minecraft-the-block-as-universal-metaphor" tabindex="-1">Minecraft: The Block as Universal Metaphor <a class="header-anchor" href="#minecraft-the-block-as-universal-metaphor" aria-label="Permalink to &quot;Minecraft: The Block as Universal Metaphor&quot;"></a></h3>
<div class="video-embed">
  <iframe src="https://www.youtube.com/embed/MmB9b5njVbA" title="Official Minecraft Trailer" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  <p class="game-caption">Official Minecraft Trailer (Mojang). The block-based world where TNT, pickaxes, and shovels double as terrain editors.</p>
</div>
<p>Minecraft's genius is that terrain editing is just inventory management. You break a block. You get a block. You place a block. The &quot;brush&quot; is a single voxel, the &quot;falloff&quot; is binary (affected or not), and the &quot;operation&quot; is add or remove.</p>
<p>TNT scales this up. You craft it from gunpowder and sand (materials you already collected while playing), place it like any other block, light it with flint and steel, and it carves a sphere out of the terrain. The blast radius is 4-7 blocks. Blast resistance varies by material: dirt and sand disintegrate, stone partially survives, obsidian is immune. Water nullifies block destruction but not entity damage.</p>
<p>The player never thinks about blast radius as a parameter. They think &quot;TNT is strong, obsidian is tough, water is a shield.&quot; The terrain editing system is fully expressed through materials and items the player already knows.</p>
<h3 id="valheim-the-farmer-s-toolkit" tabindex="-1">Valheim: The Farmer's Toolkit <a class="header-anchor" href="#valheim-the-farmer-s-toolkit" aria-label="Permalink to &quot;Valheim: The Farmer's Toolkit&quot;"></a></h3>
<div class="video-embed">
  <iframe src="https://www.youtube.com/embed/zmIS9B3w7bw" title="How to Level and Shape Terrain in Valheim" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  <p class="game-caption">A clear walkthrough of Valheim's hoe, cultivator, and pickaxe terrain-shaping vocabulary, including the "stand-where-you-want-the-ground" leveling trick.</p>
</div>
<p>Valheim takes a different approach. Instead of blocks, it gives players tools from pre-industrial farming:</p>
<p><strong>The Hoe</strong> (5 wood + 2 stone, available in the first 10 minutes) has four modes: Level Ground (flattens to your standing height), Raise Ground (adds dirt, costs 2 stone per use), Path (removes grass), and Pave (lays cobblestone). The leveling mechanic is brilliant: you stand where you want the ground to be, and the hoe makes everything around you match that height. No numeric input. Your feet are the reference.</p>
<p><strong>The Cultivator</strong> (5 bronze + 5 core wood, requires smelting) does planting: till soil, plant seeds, grow grass, and regrow vegetation. It doesn't shape terrain but it dresses it. After you flatten a hillside with the hoe, you cultivate it and plant a forest. The terrain tells a story: someone was here, they worked this land.</p>
<p><strong>The Pickaxe</strong> handles the destructive side. It digs into terrain, the inverse of the hoe's raise. The three tools together (pickaxe digs, hoe raises/flattens, cultivator plants) form a complete vocabulary that maps to real-world farming actions.</p>
<p>The cost model matters. Raising ground costs stone. You feel the weight of each edit. You don't terraform a mountain for free. The resource cost makes sculpting a decision, not a checkbox.</p>
<h3 id="animal-crossing-earned-terraforming" tabindex="-1">Animal Crossing: Earned Terraforming <a class="header-anchor" href="#animal-crossing-earned-terraforming" aria-label="Permalink to &quot;Animal Crossing: Earned Terraforming&quot;"></a></h3>
<div class="video-embed">
  <iframe src="https://www.youtube.com/embed/q9wHV8XmLfs" title="How to Unlock Terraforming in Animal Crossing: New Horizons" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  <p class="game-caption">GameXplain walks through how the Island Designer app unlocks, plus the cliff and waterscape permits that gate each new sculpting tool behind earned currency.</p>
</div>
<p>Animal Crossing: New Horizons gates terraforming behind a 3-star island rating. You need to build a campsite, invite villagers, construct housing plots, and develop your island for days (real-world days, not game-hours) before Tom Nook hands you the Island Designer app. By the time you unlock terraforming, you understand the island. You know where the rivers are, which cliffs block your favorite views, and what you want to change.</p>
<p>The tools themselves use a permit system. Paths are free. Waterscaping (creating rivers, ponds, waterfalls) costs 6,000 Nook Miles. Cliff Construction costs another 6,000. Additional path types cost 2,000 each. You're spending currency you earned through gameplay to expand your creative toolkit.</p>
<p>The editing is tile-based. One square at a time. No radius slider. No strength parameter. You push one tile of cliff up or dig one tile of river out. The constraint makes it approachable: the worst mistake you can make takes one button press to undo. The design encourages planning (the community built Happy Island Designer, a browser tool for pre-planning layouts) and incremental refinement over time.</p>
<p>Cliffs can go up to 3 levels. Rivers must connect to the ocean. Certain landmarks (airport, resident services) are immovable. These constraints aren't limitations. They're design. They prevent the &quot;blank canvas paralysis&quot; that kills casual engagement in freeform editors.</p>
<h3 id="no-man-s-sky-the-multi-tool" tabindex="-1">No Man's Sky: The Multi-Tool <a class="header-anchor" href="#no-man-s-sky-the-multi-tool" aria-label="Permalink to &quot;No Man's Sky: The Multi-Tool&quot;"></a></h3>
<div class="video-embed">
  <iframe src="https://www.youtube.com/embed/mnxMzm3_Ykw?start=942" title="No Man's Sky: Building into Terrain (Beeblebum)" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  <p class="game-caption">The Terrain Manipulator section of Beeblebum's base building guide (jumps to 15:42), showing the four sphere-based modes and the "regenerate" limit.</p>
</div>
<p>No Man's Sky puts terrain manipulation on a gun. The Terrain Manipulator is a multi-tool upgrade with four modes: Mine (removes terrain in a sphere), Create (adds terrain in sphere or cube shapes), Restore (reverts to the original generated state), and Flatten (levels large areas).</p>
<p>The metaphor is a sci-fi device that fits in your hand. You point and shoot to edit the world. T and R keys change the sphere size between three presets (small, medium, large). There's no numeric radius. Three sizes. That's the whole UI.</p>
<p>The tool is introduced in the first mission. By the time you start exploring, you already know how to carve a cave or flatten a landing pad. The create mode lets you choose terrain types (rock, dirt, etc.), which maps to the splat-map texture blending under the hood.</p>
<p>Energy drain keeps sculpting in check. In Normal mode it's generous. In Survival mode, every edit costs meaningful resources. The same tool at two difficulty levels serves both casual sculptors and resource-conscious survivalists.</p>
<h3 id="astroneer-the-vacuum-gun" tabindex="-1">Astroneer: The Vacuum Gun <a class="header-anchor" href="#astroneer-the-vacuum-gun" aria-label="Permalink to &quot;Astroneer: The Vacuum Gun&quot;"></a></h3>
<div class="video-embed">
  <iframe src="https://www.youtube.com/embed/0KXQZG7riEs" title="ASTRONEER - Release Trailer" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  <p class="game-caption">Official Astroneer release trailer (System Era). The Terrain Tool sucks soil up and blows it back out, turning sculpting and resource gathering into the same gesture.</p>
</div>
<p>Astroneer takes the simplest possible metaphor: a vacuum cleaner for dirt. Point at terrain, hold the trigger, and it sucks up soil. Point at empty space and it blows soil back out. The terrain deforms in real time around your cursor.</p>
<p>Augments modify the behavior: the Alignment Mod makes surfaces flat relative to the planet's curve. The Boost Mod increases speed. The Wide Mod increases the area of effect. The Inhibitor Mod lets you collect resources without deforming terrain at all.</p>
<p>The genius is that terrain is simultaneously a resource. The soil you vacuum up fills canisters. The soil in canisters can be processed into materials at a Soil Centrifuge. Sculpting the world and gathering resources are the same action. You don't dig because you want a cave. You dig because you need soil to make compound. The cave is a side effect that becomes useful later.</p>
<h3 id="deep-rock-galactic-mining-as-teamwork" tabindex="-1">Deep Rock Galactic: Mining as Teamwork <a class="header-anchor" href="#deep-rock-galactic-mining-as-teamwork" aria-label="Permalink to &quot;Deep Rock Galactic: Mining as Teamwork&quot;"></a></h3>
<div class="video-embed">
  <iframe src="https://www.youtube.com/embed/t9VsWTLJkVY" title="The Driller Masterclass - Deep Rock Academy" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  <p class="game-caption">Deep Rock Academy's Driller Masterclass. The forearm drills carve tunnels mid-combat, blurring the line between weapon and terrain tool.</p>
</div>
<p>Deep Rock Galactic maps terrain destruction to class roles. The Driller has power drills mounted on their forearms that chew through rock at 2m wide by 2.2m tall by 1m deep per second. The drills overheat after sustained use (5.5 seconds to red, 8 seconds cooldown) and run on limited fuel. Every dwarf has a pickaxe for precision work.</p>
<p>The Driller's job is to make the path. Carve a tunnel from point A to point B through solid rock. The team follows. The terrain editing serves the mission objective (reach the extraction pod) rather than being a creative tool. But the side effect is that every cave system the team plays through is uniquely shaped by their decisions. No two runs look the same.</p>
<p>The co-op structure makes sculpting social. The Driller doesn't dig alone. The Scout lights up the cave with flares. The Engineer places platforms on walls the Driller exposed. The Gunner defends the tunnel entrance. The terrain editing is one verb in a shared sentence.</p>
<h3 id="teardown-destruction-as-puzzle" tabindex="-1">Teardown: Destruction as Puzzle <a class="header-anchor" href="#teardown-destruction-as-puzzle" aria-label="Permalink to &quot;Teardown: Destruction as Puzzle&quot;"></a></h3>
<div class="video-embed">
  <iframe src="https://www.youtube.com/embed/mdqJEvia4mQ" title="Teardown - Gameplay Overview Trailer" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
  <p class="game-caption">Official Teardown gameplay overview (Tuxedo Labs / PlayStation). Every tool maps to a physical metaphor, and the voxel physics make consequences immediate and obvious.</p>
</div>
<p>Teardown wraps terrain destruction in heist planning. You have unlimited time to prepare: smash walls, drive vehicles through buildings, place explosives, build ramps. Then the alarm triggers and you have 60 seconds to hit every objective and escape.</p>
<p>Every tool has a physical identity. The sledgehammer breaks things up close. The blowtorch cuts steel. Explosives blow out walls. The fire extinguisher clears fire. Vehicles crash through structures. The player doesn't &quot;edit terrain&quot; in the abstract. They swing a hammer at a wall and the wall breaks.</p>
<p>The physics make it feel real. Structures collapse when support is removed. Fire spreads. Smoke fills rooms. Debris falls. The feedback loop is immediate and physical: hit something, watch it break, see the consequences cascade. Casual players understand this because they've swung a hammer in real life (or wanted to).</p>
<h3 id="eco-consequences-as-game-design" tabindex="-1">Eco: Consequences as Game Design <a class="header-anchor" href="#eco-consequences-as-game-design" aria-label="Permalink to &quot;Eco: Consequences as Game Design&quot;"></a></h3>
<p>Eco is the rare game where terrain editing has permanent, shared consequences. Cut too many trees and air quality drops. Mine too aggressively and you pollute the water table. Overhunt and species go extinct. The ecosystem is fully simulated.</p>
<p>Players can propose and vote on laws: &quot;No logging within 200m of the river&quot; or &quot;Mining tax of 10 gold per cubic meter.&quot; The governance system makes terrain editing a political act. You can't just dig wherever you want. The community might stop you.</p>
<p>This is the extreme end of gamification: the sculpting system itself becomes the core game loop. The terrain is the shared resource that every player action affects. Sculpting isn't a creative tool. It's the central tension of the game.</p>
<h2 id="design-patterns-for-cinevva" tabindex="-1">Design Patterns for Cinevva <a class="header-anchor" href="#design-patterns-for-cinevva" aria-label="Permalink to &quot;Design Patterns for Cinevva&quot;"></a></h2>
<p>These games suggest a toolkit of patterns we can use. Not all at once. In layers, starting with the most accessible and building toward the most complex.</p>
<h3 id="pattern-1-object-based-sculpting-the-tnt-pattern" tabindex="-1">Pattern 1: Object-Based Sculpting (The TNT Pattern) <a class="header-anchor" href="#pattern-1-object-based-sculpting-the-tnt-pattern" aria-label="Permalink to &quot;Pattern 1: Object-Based Sculpting (The TNT Pattern)&quot;"></a></h3>
<p>Don't give players a brush. Give them objects that happen to reshape terrain when used.</p>
<p><strong>TNT / Dynamite:</strong> Player crafts or finds an explosive. Places it in the world. Lights the fuse (or throws it). A sphere of terrain gets subtracted. The radius depends on the explosive type. Small firecracker: 2m radius. Standard dynamite: 5m. Mega bomb (rare drop): 15m. The player is blowing things up, not &quot;editing terrain.&quot;</p>
<p><strong>Pickaxe / Shovel / Drill:</strong> Handheld tools with different characteristics. The pickaxe chips away at rock (small radius, fast, works on hard materials). The shovel scoops dirt (medium radius, fast on soft ground, useless on rock). The drill bores tunnels (narrow and deep, leaves smooth cylindrical holes). Each tool maps to a different SDF brush operation under the hood, but the player experiences them as distinct physical objects with distinct feedback.</p>
<p><strong>Bucket / Watering Can:</strong> Pour water on terrain and it flows downhill, eroding a channel. The erosion brush follows the terrain gradient automatically. The player doesn't aim a brush. They pour water and watch the river form. Under the hood it's a smooth-lower operation biased by the terrain slope.</p>
<p><strong>Seeds / Saplings:</strong> Plant them and terrain grows. A seed packet placed on flat ground sprouts into a small hill over time (over several seconds, with an animation). A tree sapling placed on bare rock generates a mound of soil around its base. &quot;Growing&quot; terrain is just the raise brush with a nature skin.</p>
<p><strong>Terraform Beacon:</strong> Place a beacon and it slowly flattens everything within its radius to a target height over time. Good for building sites. The player places the object and walks away. The flatten brush runs automatically at low intensity until the ground matches the beacon height.</p>
<h3 id="pattern-2-tool-progression-the-stardew-pattern" tabindex="-1">Pattern 2: Tool Progression (The Stardew Pattern) <a class="header-anchor" href="#pattern-2-tool-progression-the-stardew-pattern" aria-label="Permalink to &quot;Pattern 2: Tool Progression (The Stardew Pattern)&quot;"></a></h3>
<p>Start players with weak tools that do small edits. Upgrade through gameplay.</p>
<p><strong>Tier 1 (Free, immediate):</strong> Bare hands. Can pat down small bumps (micro-smooth, 0.5m radius). Can scoop up a handful of dirt (micro-lower, 0.3m radius). Negligible impact. Teaches the system exists.</p>
<p><strong>Tier 2 (Crafted from basic materials):</strong> Stone shovel, wooden pickaxe. Usable for small edits. Flatten a campsite. Dig a shallow trench. 1-2m radius. This is where most casual players live.</p>
<p><strong>Tier 3 (Requires exploration or trading):</strong> Metal tools. Larger radius (3-5m). New operations unlocked: raise terrain with the hoe, carve smooth curves with the chisel.</p>
<p><strong>Tier 4 (Endgame / rare drops):</strong> Power tools, explosives, terraform devices. 10-20m radius. Cave boring. Mountain building. These are spectacle tools that reward long-term players.</p>
<p>The upgrade path doubles as a tutorial. Each tier introduces one new concept. By the time you reach Tier 4, you've internalized the vocabulary of terrain editing through incremental exposure, not a tutorial screen.</p>
<h3 id="pattern-3-contextual-tools-the-valheim-pattern" tabindex="-1">Pattern 3: Contextual Tools (The Valheim Pattern) <a class="header-anchor" href="#pattern-3-contextual-tools-the-valheim-pattern" aria-label="Permalink to &quot;Pattern 3: Contextual Tools (The Valheim Pattern)&quot;"></a></h3>
<p>Match the tool to what the player is trying to do, not what the terrain needs.</p>
<p><strong>Building mode:</strong> When the player is in building mode (placing walls, floors, roofs), the terrain auto-flattens under foundations. They don't need a separate flatten tool. The building system handles it. This is how Valheim's building works: place a floor, and the ground adjusts.</p>
<p><strong>Farming mode:</strong> When planting crops, the cultivator tills the soil (changes the surface material) and levels small bumps. The player is farming, not terraforming. The terrain editing is a side effect of the agricultural action.</p>
<p><strong>Mining mode:</strong> When harvesting resources from terrain (ore veins, clay deposits, mineral nodes), the extraction leaves a hole. The terrain editing is a side effect of resource gathering. Astroneer does this perfectly: vacuum up soil, get resources, leave a cave.</p>
<p><strong>Combat mode:</strong> Explosions from weapons create craters. Fire burns away vegetation. Acid dissolves rock. Terrain damage is a side effect of combat. Deep Rock Galactic's drills work this way: they're a weapon and a terrain tool simultaneously.</p>
<h3 id="pattern-4-social-sculpting-the-eco-pattern" tabindex="-1">Pattern 4: Social Sculpting (The Eco Pattern) <a class="header-anchor" href="#pattern-4-social-sculpting-the-eco-pattern" aria-label="Permalink to &quot;Pattern 4: Social Sculpting (The Eco Pattern)&quot;"></a></h3>
<p>In a shared world, every edit is a social act. Design for that.</p>
<p><strong>Visible authorship:</strong> When a player sculpts terrain, leave a subtle indicator of who did it. A faint glow. A player-colored tint on the edited surface. A small flag. Other players can see that someone was here and changed the landscape.</p>
<p><strong>Collaborative projects:</strong> Terrain edits within a claimed area accumulate toward a shared goal. &quot;Dig a river from the mountain to the lake.&quot; Progress bar visible to everyone in the area. When complete, the river fills with water. The sculpting was a group activity with a shared reward.</p>
<p><strong>Approval zones:</strong> In protected areas, terrain edits require approval from the landowner or a vote from nearby players. This prevents griefing without disabling sculpting. The edit is previewed as a ghost (transparent, outlined) until approved.</p>
<p><strong>Undo by consensus:</strong> Any player can propose reverting a terrain edit. If enough nearby players agree, the edit fades away over a few seconds. The restore mode from No Man's Sky, but social.</p>
<h3 id="pattern-5-natural-forces-the-erosion-pattern" tabindex="-1">Pattern 5: Natural Forces (The Erosion Pattern) <a class="header-anchor" href="#pattern-5-natural-forces-the-erosion-pattern" aria-label="Permalink to &quot;Pattern 5: Natural Forces (The Erosion Pattern)&quot;"></a></h3>
<p>Instead of direct brush strokes, let players trigger natural processes.</p>
<p><strong>Rain summoner:</strong> Place a cloud above a hill and it rains. The rain erodes the hillside over time, cutting channels and softening edges. The player controls where the rain falls and how long it lasts. The erosion shader does the sculpting.</p>
<p><strong>Earthquake device:</strong> Triggers a local earthquake that cracks the ground, creates fissures, and lowers the terrain in the affected area. The player chooses where. The physics simulation determines how.</p>
<p><strong>Lava flow:</strong> Release lava from a point and it flows downhill, filling valleys and hardening into rock. New terrain created by a natural metaphor. Under the hood it's the raise brush following the terrain gradient with a rock material.</p>
<p><strong>Wind sculptor:</strong> A fan or blowing device that pushes soft terrain (sand, dirt) in the wind direction. Over time, it creates dunes and smooth slopes. The smooth brush with a directional bias.</p>
<p><strong>Freeze / Thaw:</strong> Freeze water in a river to create an ice bridge. Thaw a frozen hillside to trigger a mudslide. Temperature as a terrain editing parameter, expressed through weather items.</p>
<h3 id="pattern-6-discovery-and-surprise-the-infinite-craft-pattern" tabindex="-1">Pattern 6: Discovery and Surprise (The Infinite Craft Pattern) <a class="header-anchor" href="#pattern-6-discovery-and-surprise-the-infinite-craft-pattern" aria-label="Permalink to &quot;Pattern 6: Discovery and Surprise (The Infinite Craft Pattern)&quot;"></a></h3>
<p>Reward experimentation.</p>
<p><strong>Combination effects:</strong> TNT near water creates a hot spring (crater + water fill + steam particles). Seeds planted in TNT craters grow faster (the crater has &quot;fertile soil&quot;). A frozen river that gets dynamited reveals a cave underneath.</p>
<p><strong>Hidden materials:</strong> Digging deep enough reveals different terrain layers: topsoil, clay, stone, crystal, lava. Each layer has different properties and looks. Players who dig deeper see things nobody else has seen.</p>
<p><strong>Fossils and artifacts:</strong> Random terrain edits occasionally unearth buried objects: fossils, ancient artifacts, treasure chests, rare seeds. The act of sculpting becomes an exploration mechanic. You don't just dig for the cave. You dig because you might find something.</p>
<p><strong>Terrain memory:</strong> Edited terrain remembers what happened to it. An area that was blown up by TNT has scorch marks and charred edges. An area that was grown with seeds has richer soil color. An area that was eroded by rain has smooth, water-worn textures. The terrain tells its own history.</p>
<h2 id="the-brush-to-metaphor-mapping" tabindex="-1">The Brush-to-Metaphor Mapping <a class="header-anchor" href="#the-brush-to-metaphor-mapping" aria-label="Permalink to &quot;The Brush-to-Metaphor Mapping&quot;"></a></h2>
<p>Every metaphor the player interacts with maps to a technical brush operation from our <a href="/guides/terrain-sculpting-brushes.html">sculpting pipeline</a>. Here's the translation table:</p>
<p><strong>Pickaxe / Shovel</strong> maps to the <strong>lower brush</strong> (subtract from heightmap or SDF) with small radius, high strength, sharp falloff. The tool animation triggers the same <code>Chunk.applyBrush()</code> call.</p>
<p><strong>TNT / Explosives</strong> map to the <strong>lower brush</strong> with large radius, maximum strength, sphere falloff. A single explosive event applies one brush stroke centered on the detonation point. Blast resistance per material type maps to a strength multiplier on the brush.</p>
<p><strong>Hoe / Flattener</strong> maps to the <strong>flatten brush</strong> with medium radius, smooth falloff. The target height is sampled from the player's foot position when the tool is activated, exactly like Valheim.</p>
<p><strong>Seeds / Growth</strong> maps to the <strong>raise brush</strong> with small radius, smooth falloff, applied over several frames (animated growth). Each frame applies a small increment. The animation sells the metaphor while the brush handles the geometry.</p>
<p><strong>Watering Can / Rain</strong> maps to the <strong>smooth brush</strong> with directional bias. The bias follows the terrain slope gradient from <code>ChunkManager.getNormal()</code>. Points below the brush center get lowered more than points above it, creating natural erosion channels.</p>
<p><strong>Terraform Beacon</strong> maps to the <strong>flatten brush</strong> applied at low strength every frame until convergence. The beacon's world-space position sets the target height. The radius is the beacon's area of effect.</p>
<p><strong>Drill</strong> maps to the <strong>lower brush</strong> projected along a ray direction instead of a sphere. The ray cast from the player's aim direction determines the brush center at each depth step, creating a cylindrical tunnel.</p>
<h2 id="progression-and-economy" tabindex="-1">Progression and Economy <a class="header-anchor" href="#progression-and-economy" aria-label="Permalink to &quot;Progression and Economy&quot;"></a></h2>
<h3 id="what-players-earn" tabindex="-1">What Players Earn <a class="header-anchor" href="#what-players-earn" aria-label="Permalink to &quot;What Players Earn&quot;"></a></h3>
<p><strong>Tool unlocks:</strong> Start with hands. Craft basic tools from common materials. Discover recipes for advanced tools through exploration, trading, or quest completion.</p>
<p><strong>Capacity upgrades:</strong> Bigger explosives. Wider hoes. Faster drills. The brush radius and strength increase with tier. Upgrading your shovel from wood to iron doubles the radius.</p>
<p><strong>Material types:</strong> New terrain materials unlock as players explore biomes. You can only &quot;create&quot; terrain types you've encountered. Visit a desert to unlock sand creation. Visit a volcano to unlock basite. The create mode from No Man's Sky, gated by exploration.</p>
<p><strong>Recipes:</strong> Combination effects are discovered, not given. Plant a seed in a TNT crater and discover &quot;Fertile Blast.&quot; Pour water on lava terrain and discover &quot;Obsidian Formation.&quot; Each discovery is cataloged and shareable.</p>
<h3 id="what-it-costs" tabindex="-1">What It Costs <a class="header-anchor" href="#what-it-costs" aria-label="Permalink to &quot;What It Costs&quot;"></a></h3>
<p><strong>Resources:</strong> Every terrain edit consumes materials. Raising terrain costs dirt/stone/sand (the material being placed). Lowering terrain yields materials (which go to your inventory). Flattening redistributes. The resource flow keeps the economy connected to the terrain system.</p>
<p><strong>Energy / Durability:</strong> Tools have durability or energy limits. The pickaxe breaks after N uses. The terraform beacon runs on fuel. This prevents infinite editing and creates natural session boundaries. Players take breaks to resupply.</p>
<p><strong>Time:</strong> Some operations are instant (pickaxe swing). Others are slow (beacon flattening, seed growing, rain erosion). Slow operations reward patience and discourage spam. They also look better: watching a seed sprout into a hill is more satisfying than clicking a &quot;raise&quot; button.</p>
<h3 id="what-others-see" tabindex="-1">What Others See <a class="header-anchor" href="#what-others-see" aria-label="Permalink to &quot;What Others See&quot;"></a></h3>
<p><strong>Edit footprint:</strong> Every terrain modification has a visual signature. TNT craters have dark, cracked edges. Hoe-flattened areas have a smooth, worked-earth texture. Grown terrain has rich, organic coloring. Rain-eroded channels have watermarks. Players can read the history of the landscape.</p>
<p><strong>Creator tags:</strong> Hover over edited terrain to see who changed it and when. &quot;Sculpted by @PlayerName, 2 hours ago.&quot; This creates pride of authorship and social accountability.</p>
<p><strong>Before/After:</strong> A &quot;time lapse&quot; view that shows the terrain at any point in its edit history. Scrub a timeline slider to see the landscape transform. This is possible because the CSG edit tree from Phase 3 of the <a href="/guides/terrain-sculpting-brushes.html">technical guide</a> stores every operation in order.</p>
<h2 id="what-we-build-first" tabindex="-1">What We Build First <a class="header-anchor" href="#what-we-build-first" aria-label="Permalink to &quot;What We Build First&quot;"></a></h2>
<p>Phase 1 of the <a href="/guides/terrain-sculpting-brushes.html#practical-architecture-for-sculpting">technical architecture</a> works with heightmap sculpting on the existing WebGL renderer. The casual tools we can ship on top of that:</p>
<ol>
<li><strong>Shovel</strong> (lower brush, small radius). The most basic tool. Click terrain, dig a hole. Yields dirt resources.</li>
<li><strong>Hoe</strong> (flatten brush, medium radius). Stand where you want the ground, use the hoe, terrain levels to your feet.</li>
<li><strong>TNT</strong> (lower brush, large radius, one-shot). Craft from materials, place, ignite, watch the boom. Yields rubble.</li>
<li><strong>Seeds</strong> (raise brush, animated over frames). Plant on flat ground, watch terrain rise into a small hill, vegetation appears on top.</li>
<li><strong>Watering Can</strong> (smooth brush with slope bias). Pour on slopes to erode them. Creates natural-looking channels.</li>
</ol>
<p>Five tools. Five distinct metaphors. One underlying brush system. Each tool is a different configuration of <code>(radius, strength, falloff, operation, animation)</code> wrapped in an item the player crafts, equips, and uses through the same interaction model as the existing <code>PlacementTool</code>.</p>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[3D Brush Techniques and In-Game World Sculpting]]></title>
            <link>https://app.cinevva.com/guides/terrain-sculpting-brushes</link>
            <guid>https://app.cinevva.com/guides/terrain-sculpting-brushes</guid>
            <pubDate>Sun, 05 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[From heightmap displacement to SDF CSG trees. How terrain sculpting brushes actually work, what marching cubes and surface nets do under the hood, and how to let players reshape a shared world in real time.]]></description>
            <content:encoded><![CDATA[<h1 id="_3d-brush-techniques-and-in-game-world-sculpting" tabindex="-1">3D Brush Techniques and In-Game World Sculpting <a class="header-anchor" href="#_3d-brush-techniques-and-in-game-world-sculpting" aria-label="Permalink to &quot;3D Brush Techniques and In-Game World Sculpting&quot;"></a></h1>
<p>We want players to sculpt the world. Not place prefabs on a grid. Not toggle blocks on and off. Actually reshape the terrain: carve rivers, raise mountains, smooth cliffsides, dig caves. The kind of thing that ZBrush and Blender's sculpt mode do for artists, but running at 60fps inside a multiplayer browser game.</p>
<p>That's a hard engineering problem that spans data representation, mesh extraction, GPU compute, brush mathematics, and network synchronization. This guide documents everything we found.</p>
<h2 id="the-two-worlds-of-terrain-representation" tabindex="-1">The Two Worlds of Terrain Representation <a class="header-anchor" href="#the-two-worlds-of-terrain-representation" aria-label="Permalink to &quot;The Two Worlds of Terrain Representation&quot;"></a></h2>
<p>Every sculpting system starts with a choice about how terrain data is stored. That choice determines what kinds of edits are possible, how fast they run, and how much memory they cost.</p>
<h3 id="heightmaps" tabindex="-1">Heightmaps <a class="header-anchor" href="#heightmaps" aria-label="Permalink to &quot;Heightmaps&quot;"></a></h3>
<p>A heightmap stores one height value per grid point. You can think of it as a grayscale image where brightness equals elevation. Our current <code>world/client</code> terrain works exactly this way: <code>noise.ts</code> generates height via FBM value noise, and each <code>Chunk</code> stores a <code>Float32Array</code> heightmap that gets projected onto a <code>PlaneGeometry</code>.</p>
<p>Heightmaps are fast. Sampling is a single array lookup with bilinear interpolation. LOD is trivial because you just reduce the grid resolution. Splat-based texture blending maps directly onto the UV grid. Physics collisions reduce to a height query.</p>
<p>The limitation is topology. A heightmap can only represent one height per (x, z) coordinate. No caves. No overhangs. No arches. No tunnels. If a player sculpts a cliff that folds back on itself, a heightmap can't store it. For terrain that's mostly rolling hills and mountains, this is fine. For freeform sculpting where players can dig into the ground, it's a dead end.</p>
<h3 id="volumetric-3d-scalar-fields" tabindex="-1">Volumetric (3D Scalar Fields) <a class="header-anchor" href="#volumetric-3d-scalar-fields" aria-label="Permalink to &quot;Volumetric (3D Scalar Fields)&quot;"></a></h3>
<p>The alternative is storing a value at every point in 3D space. If the value is negative inside solid material and positive outside (or vice versa), you have a Signed Distance Field (SDF). If the value is just a density (above some threshold is solid, below is empty), you have a density field.</p>
<p>Volumetric representations handle any topology. Caves, overhangs, floating islands, tunnels through mountains. The tradeoff is memory and complexity. A 256^3 grid with 32-bit floats costs 64 MB. A 512^3 grid costs 512 MB. And that's for a single chunk. You need sparse data structures (octrees, brick maps) to make this practical.</p>
<p>The mesh extraction step is also nontrivial. You can't just set vertex Y positions and be done. You need an algorithm that reads the scalar field and produces a triangle mesh that approximates the surface where the field crosses zero.</p>
<h2 id="mesh-extraction-marching-cubes-surface-nets-and-dual-contouring" tabindex="-1">Mesh Extraction: Marching Cubes, Surface Nets, and Dual Contouring <a class="header-anchor" href="#mesh-extraction-marching-cubes-surface-nets-and-dual-contouring" aria-label="Permalink to &quot;Mesh Extraction: Marching Cubes, Surface Nets, and Dual Contouring&quot;"></a></h2>
<h3 id="marching-cubes" tabindex="-1">Marching Cubes <a class="header-anchor" href="#marching-cubes" aria-label="Permalink to &quot;Marching Cubes&quot;"></a></h3>
<p>Marching Cubes is the oldest and most widely implemented isosurface extraction algorithm. Published by Lorensen and Cline in 1987, it works by examining every cube in the voxel grid where each corner has a scalar value. If some corners are inside the surface (negative) and some are outside (positive), a triangle mesh patch gets placed inside that cube.</p>
<p>Each cube has 8 corners, each either inside or outside, producing 256 possible configurations (2^8). These reduce to 15 unique cases via symmetry. A lookup table maps each case to a set of triangles. Edge intersection points are found by linear interpolation along the edges where the sign changes.</p>
<p><strong>Recent GPU implementations</strong> have made Marching Cubes fast enough for real-time sculpting. A 2025 implementation on UE5 assigns each GPU thread to one cube, processing thousands simultaneously. The key insight is that each cube's triangulation is independent of its neighbors, making the algorithm embarrassingly parallel.</p>
<p><strong>MCHex</strong> (arxiv 2511.02064, 2025) extends Marching Cubes to adaptive hexahedral mesh generation with guaranteed positive Jacobian values, improving boundary approximation for simulation meshes.</p>
<p><strong>rupMC</strong> achieves dozens of times faster performance than serial implementations and 4x faster than parallel DMC variants using CPU/GPU heterogeneous architectures.</p>
<p>The main limitation: Marching Cubes struggles with sharp features. A 90-degree edge gets rounded into a smooth curve. For terrain sculpting this is usually acceptable (natural terrain is mostly smooth), but for architectural features it's a problem.</p>
<h3 id="surface-nets" tabindex="-1">Surface Nets <a class="header-anchor" href="#surface-nets" aria-label="Permalink to &quot;Surface Nets&quot;"></a></h3>
<p>Surface Nets is a newer family of algorithms that produces smoother meshes from discrete scalar fields. Instead of placing vertices on cube edges (like Marching Cubes), Surface Nets places one vertex per cube that contains the surface, then connects adjacent vertices to form quads.</p>
<p>The result is naturally smoother. A 2024 paper (arxiv 2401.14906) demonstrated a high-performance parallel Surface Nets implementation that runs one to two orders of magnitude faster than sequential algorithms. The <code>fast-surface-nets</code> Rust crate generates roughly 20 million triangles per second on a single 2.5 GHz core using small lookup tables and SIMD acceleration.</p>
<p><code>bevy-sculpter</code> (v0.18.0, January 2026) uses Surface Nets as its primary meshing strategy. The crate provides SDF-based volumetric sculpting with four brush types: hard CSG (instant add/remove), smooth continuous (for held input), blur (surface smoothing), and flatten (set to target height). It also includes SDF redistancing via the Fast Sweeping Method to restore proper signed distance field properties after edits.</p>
<p>Surface Nets are a good middle ground between Marching Cubes (simple, fast, but produces aliased meshes on binary data) and Dual Contouring (feature-preserving but complex).</p>
<h3 id="dual-contouring" tabindex="-1">Dual Contouring <a class="header-anchor" href="#dual-contouring" aria-label="Permalink to &quot;Dual Contouring&quot;"></a></h3>
<p>Dual Contouring preserves sharp features that Marching Cubes and Surface Nets can't. It does this by using not just the sign of the field at each corner, but also the gradient (normal) at edge intersections. A QEF (Quadratic Error Function) minimization places the vertex inside each cell at the position that best satisfies all the edge intersection constraints.</p>
<p>The result: sharp edges and corners are preserved in the extracted mesh. A cube with perpendicular faces stays a cube.</p>
<p>The tradeoff is complexity. The QEF solve has inter-cell dependencies that make GPU parallelization hard. It can produce non-manifold meshes (edges shared by more than two polygons). And the implementation is more involved than Marching Cubes, though Johannes Jendersie notes that a working Dual Contouring implementation is about 200 lines of code versus 500+ for a robust Marching Cubes.</p>
<p><strong>Cubical Marching Squares (CMS)</strong> has been proposed as a middle ground: intercell-independent (GPU-friendly) while still offering some feature preservation.</p>
<h3 id="which-to-use" tabindex="-1">Which to Use <a class="header-anchor" href="#which-to-use" aria-label="Permalink to &quot;Which to Use&quot;"></a></h3>
<p>For player-facing terrain sculpting in a browser:</p>
<p><strong>Surface Nets</strong> is the strongest candidate. It produces smooth meshes (natural-looking terrain) without the aliasing artifacts of Marching Cubes on binary data. It's fast enough for real-time re-meshing. And it's simpler to implement than Dual Contouring.</p>
<p><strong>Marching Cubes</strong> remains a solid choice when GPU parallelism is the priority (every cube is independent) or when you need the widest library support. The WebGPU SDF Editor by Reinder Nijhoff (January 2026) implements both Marching Cubes and Surface Nets in its extraction pipeline, running entirely on the GPU.</p>
<p><strong>Dual Contouring</strong> is best reserved for cases where architectural precision matters more than performance. Not ideal for real-time terrain sculpting in a browser context.</p>
<h2 id="the-transvoxel-algorithm-solving-lod-stitching" tabindex="-1">The Transvoxel Algorithm: Solving LOD Stitching <a class="header-anchor" href="#the-transvoxel-algorithm-solving-lod-stitching" aria-label="Permalink to &quot;The Transvoxel Algorithm: Solving LOD Stitching&quot;"></a></h2>
<p>When voxel terrain is meshed at different resolutions (LOD0 near the player, LOD2 far away), cracks form at the boundaries. For heightmaps this is a simple problem: interpolate edge vertices to match the lower-resolution neighbor. Our current <code>chunk.ts</code> does exactly this in <code>stitchEdge()</code>.</p>
<p>For volumetric terrain, the problem is much harder. A cave mouth at LOD0 might produce 30 triangles on the boundary. The same region at LOD1 might produce 8 triangles with a completely different topology. There's no simple way to linearly interpolate between them.</p>
<p>Eric Lengyel's Transvoxel Algorithm (2009) solves this with &quot;transition cells.&quot; At the boundary between two LOD levels, the algorithm considers 9 high-resolution samples (instead of 8 cube corners), producing 512 possible configurations that fall into 73 equivalence classes. Each class maps to a predefined triangle pattern that perfectly fills the gap between the two resolutions.</p>
<p>The algorithm operates on local voxel data, so retriangulating a modified region is fast. This is critical for real-time sculpting: when a player edits terrain near an LOD boundary, only the transition cells need to be rebuilt.</p>
<p>A Rust implementation exists as the <code>transvoxel</code> crate. The original lookup tables are available at transvoxel.org.</p>
<h2 id="brush-mathematics" tabindex="-1">Brush Mathematics <a class="header-anchor" href="#brush-mathematics" aria-label="Permalink to &quot;Brush Mathematics&quot;"></a></h2>
<p>A sculpting brush is a function that modifies scalar field values within a radius around a target point. The mathematics are surprisingly similar across all implementations, from Blender to Unreal Engine to runtime game systems.</p>
<h3 id="falloff-functions" tabindex="-1">Falloff Functions <a class="header-anchor" href="#falloff-functions" aria-label="Permalink to &quot;Falloff Functions&quot;"></a></h3>
<p>The brush falloff determines how the edit strength decreases from the center to the edge. Blender 5.1 defines these standard profiles:</p>
<p><strong>Smooth:</strong> <code>f(d) = 3d^2 - 2d^3</code> (Hermite interpolation, same smoothstep as our noise.ts)</p>
<p><strong>Sphere:</strong> Strong at center with steep falloff near border. Approximated as <code>f(d) = sqrt(1 - d^2)</code>.</p>
<p><strong>Sharp:</strong> <code>f(d) = (1 - d)^n</code> with n &gt; 2. Creates a fine point.</p>
<p><strong>Linear:</strong> <code>f(d) = 1 - d</code> where d is normalized distance from center (0 at center, 1 at edge).</p>
<p><strong>Constant:</strong> <code>f(d) = 1</code> for <code>d &lt; 1</code>, hard cutoff at the brush boundary.</p>
<p><strong>Inverse Square:</strong> Hybrid between smooth and sphere for a natural &quot;clay-like&quot; feel.</p>
<p>In all cases, <code>d = distance_to_center / brush_radius</code>, clamped to [0, 1]. The falloff value multiplies the brush strength to produce the actual field modification at each point.</p>
<h3 id="falloff-space" tabindex="-1">Falloff Space <a class="header-anchor" href="#falloff-space" aria-label="Permalink to &quot;Falloff Space&quot;"></a></h3>
<p>Blender distinguishes between <strong>sphere falloff</strong> (distance computed in 3D world space) and <strong>projected falloff</strong> (distance computed in 2D screen space). Projected falloff means that two points that appear close on screen affect each other equally, even if they're at very different depths in world space. For terrain sculpting, 3D world space falloff is usually more intuitive.</p>
<h3 id="core-brush-operations" tabindex="-1">Core Brush Operations <a class="header-anchor" href="#core-brush-operations" aria-label="Permalink to &quot;Core Brush Operations&quot;"></a></h3>
<p><strong>Raise/Lower (Displacement):</strong> Add or subtract from the scalar field within the brush radius, weighted by falloff. For heightmaps: <code>height[i] += strength * falloff(d)</code>. For SDFs: <code>sdf[i] -= strength * falloff(d)</code> (subtracting makes material more solid, raising the surface).</p>
<p><strong>Smooth (Laplacian):</strong> Replace each value with the average of its neighbors, weighted by falloff. This erases detail and reduces noise. The Laplacian filter samples a small kernel (3x3 for heightmaps, 3x3x3 for volumes) and blends toward the mean. HC (Humphrey's Classes) smoothing is a variant that preserves volume better than raw Laplacian.</p>
<p><strong>Flatten:</strong> Set the field value to a target height (or distance in SDF space), blended by falloff. The target is usually sampled at the brush center when the stroke begins, then held constant. This creates flat plateaus.</p>
<p><strong>Pinch/Inflate:</strong> Move vertices toward or away from the surface normal. In SDF space, this is equivalent to displacing along the gradient direction.</p>
<p><strong>Grab:</strong> Translate a region of the field, as if you were pulling clay. The displacement vector is the mouse delta projected into world space, applied to field values within the radius.</p>
<p><strong>Noise:</strong> Add procedural noise to the field within the brush radius. Useful for roughening smooth surfaces.</p>
<p><strong>Stamp:</strong> Apply a 2D grayscale image as displacement, projecting it onto the surface under the cursor. Unreal Engine's Landscape tool supports this for terrain brushes.</p>
<h3 id="adaptive-tessellation" tabindex="-1">Adaptive Tessellation <a class="header-anchor" href="#adaptive-tessellation" aria-label="Permalink to &quot;Adaptive Tessellation&quot;"></a></h3>
<p><code>sculpt-3D</code> (React + Three.js browser sculpting) implements adaptive tessellation: as the brush moves across the mesh, triangles near the brush center are subdivided to provide more vertices for deformation. This prevents the &quot;low-poly stretch&quot; problem where a coarse mesh gets distorted by sculpting. The subdivision uses symmetric splitting for uniform triangle quality.</p>
<p>For volumetric systems, adaptive tessellation isn't needed in the same way because the mesh is regenerated from the field. Instead, you can locally increase the voxel resolution near edits (adaptive octrees) for the same effect.</p>
<h2 id="sdf-sculpting-the-dreams-approach" tabindex="-1">SDF Sculpting: The Dreams Approach <a class="header-anchor" href="#sdf-sculpting-the-dreams-approach" aria-label="Permalink to &quot;SDF Sculpting: The Dreams Approach&quot;"></a></h2>
<p>Media Molecule's Dreams (PS4, 2020) is the most ambitious in-game sculpting system ever shipped. Alex Evans presented the technical approach at SIGGRAPH 2015.</p>
<h3 id="representation" tabindex="-1">Representation <a class="header-anchor" href="#representation" aria-label="Permalink to &quot;Representation&quot;"></a></h3>
<p>Dreams stores geometry as a compound SDF function in 83^3 fp16 volume texture blocks. Each sculpt is a list of 1 to 100,000 &quot;edits,&quot; where each edit is a CSG operation (add, subtract, color) with a primitive shape (sphere, cube, cylinder, cone, ellipsoid, torus, etc.) and a blend mode.</p>
<p>Blend modes use soft-max and soft-min functions. A &quot;soft&quot; blend produces rounded transitions between primitives (like clay pushed together). A &quot;hard&quot; blend produces crisp boolean cuts. The blend radius is user-controllable.</p>
<h3 id="rendering" tabindex="-1">Rendering <a class="header-anchor" href="#rendering" aria-label="Permalink to &quot;Rendering&quot;"></a></h3>
<p>Dreams doesn't extract a triangle mesh. Instead, it renders directly from the SDF using a custom point-cloud renderer (&quot;flecks&quot;). Each fleck is a tiny disc oriented along the surface normal. The SDF is sampled to find surfaces, and flecks are distributed across them. This avoids the mesh extraction bottleneck entirely but requires a custom renderer.</p>
<p>For a Three.js/WebGL world, this approach isn't directly applicable. We'd need to extract meshes. But the CSG edit list concept is highly relevant for undo/redo and network synchronization.</p>
<h2 id="mike-turitzin-s-dynamic-sdf-engine-2026" tabindex="-1">Mike Turitzin's Dynamic SDF Engine (2026) <a class="header-anchor" href="#mike-turitzin-s-dynamic-sdf-engine-2026" aria-label="Permalink to &quot;Mike Turitzin's Dynamic SDF Engine (2026)&quot;"></a></h2>
<p>A game engine currently in development by Mike Turitzin uses dynamic SDFs as the core representation. The engine supports:</p>
<p><strong>Detailed modifications during gameplay:</strong> Adding and removing matter smoothly or with sharp edges. Non-destructive changes like moving holes or creating temporary tunnels that disappear behind the player.</p>
<p><strong>Brick maps and brick atlases</strong> for sparse caching. Instead of storing the entire SDF field in a dense 3D grid, the field is divided into &quot;bricks&quot; (small 3D tiles). Only bricks that contain the surface boundary are allocated. This dramatically reduces memory for scenes with mostly empty or solid space.</p>
<p><strong>Geometry clipmaps</strong> (Losasso &amp; Hoppe, SIGGRAPH 2004) for LOD. Nested regular grids at increasing resolution surround the camera position. The innermost grid has the finest resolution; outer grids are progressively coarser. This provides dramatic memory reduction while supporting vast spaces. Clipmaps update incrementally as the camera moves, making them efficient for streaming open worlds.</p>
<p><strong>Physics and collision</strong> work directly against the SDF. Sphere-tracing (ray marching with the SDF distance as the step size) provides efficient raycasting. Collision detection uses the SDF gradient as the surface normal and the distance value as the penetration depth.</p>
<h2 id="teardown-voxel-destruction-at-scale" tabindex="-1">Teardown: Voxel Destruction at Scale <a class="header-anchor" href="#teardown-voxel-destruction-at-scale" aria-label="Permalink to &quot;Teardown: Voxel Destruction at Scale&quot;"></a></h2>
<p>Teardown (Voxagon) represents the other end of the spectrum: every object in the world is a voxel volume that can be destroyed piece by piece.</p>
<h3 id="architecture" tabindex="-1">Architecture <a class="header-anchor" href="#architecture" aria-label="Permalink to &quot;Architecture&quot;"></a></h3>
<p>Objects are stored as voxel grids on regular spacing. The engine doesn't use Marching Cubes or SDFs for rendering. Instead, it ray-traces voxels directly using a modified DDA (Digital Differential Analyzer) algorithm in fragment shaders, built on OpenGL 3.3. Mipmaps form a dense octree structure for accelerating empty space traversal during ray intersection.</p>
<p>For each object, the engine rasterizes its oriented bounding box (OBB) and traces a ray through it to find voxel intersections. Only backfaces of the OBB are rendered, allowing the camera to clip into the bounding volume.</p>
<h3 id="destruction-synchronization-multiplayer" tabindex="-1">Destruction Synchronization (Multiplayer) <a class="header-anchor" href="#destruction-synchronization-multiplayer" aria-label="Permalink to &quot;Destruction Synchronization (Multiplayer)&quot;"></a></h3>
<p>Teardown's March 2026 multiplayer update uses a semi-deterministic approach. Structural destruction (cutting holes, changing ownership, reconnecting joints) is handled via fixed-point integer math on a reliable network stream. All clients execute the same deterministic commands and arrive at the same world state. Non-structural changes (debris, particles) use unreliable state synchronization.</p>
<p>This is an important insight for our multiplayer world: terrain edits must be deterministic. If Player A sculpts a mountain, all clients must produce the same mesh from the same field data. The edit commands (brush position, radius, strength, operation type) should be the authoritative data, not the resulting mesh.</p>
<h2 id="alice-sdf-compression-and-csg-trees" tabindex="-1">ALICE-SDF: Compression and CSG Trees <a class="header-anchor" href="#alice-sdf-compression-and-csg-trees" aria-label="Permalink to &quot;ALICE-SDF: Compression and CSG Trees&quot;"></a></h2>
<p>ALICE-SDF (Adaptive Lightweight Implicit Compression Engine, v1.3.0 March 2026) provides a Rust implementation of SDF-based spatial data with 10-1000x compression versus polygon meshes. It supports:</p>
<p><strong>126 building blocks:</strong> 72 primitives, 24 operations, 7 transforms, and 23 modifiers. Smooth blending operations (union, subtraction, intersection), chamfer and stairs blends for hard-edge bevels and stepped CSG transitions.</p>
<p><strong>CSG tree diff/patch</strong> for undo/redo and network synchronization. This is the key feature for multiplayer sculpting: instead of sending the entire field state, you send the structural diff between two CSG trees. The client applies the patch to reconstruct the new state. This is far more bandwidth-efficient than delta-compressing raw voxel data.</p>
<p><strong>CSG tree optimization</strong> including identity transform removal, nested transform merging, and modifier demotion. This keeps the tree compact as edits accumulate.</p>
<p><strong>Mesh generation</strong> via both Marching Cubes and Dual Contouring. Physics collision detection works directly against the SDF.</p>
<p><strong>WebAssembly support</strong> enables browser integration. The engine is written in Rust with WASM bindings, making it a realistic option for a Three.js application.</p>
<h2 id="webgpu-compute-for-terrain" tabindex="-1">WebGPU Compute for Terrain <a class="header-anchor" href="#webgpu-compute-for-terrain" aria-label="Permalink to &quot;WebGPU Compute for Terrain&quot;"></a></h2>
<p>WebGPU is now available across all major browsers as of late 2025. Chrome 113+, Edge 113+, Firefox 141+, and Safari 26+ all ship with it enabled. This opens up compute shader pipelines that were previously GPU-only.</p>
<h3 id="performance" tabindex="-1">Performance <a class="header-anchor" href="#performance" aria-label="Permalink to &quot;Performance&quot;"></a></h3>
<p>WebGPU compute shaders generate terrain roughly 100x faster than CPU methods through massive parallelization. The GPU executes thousands of calculations simultaneously, and terrain generation is almost entirely parallel (each vertex/voxel is independent).</p>
<p>Work is organized into three tiers: Dispatch Level (workload distribution across the GPU), Workgroup Level (shared memory within a processing unit), and Thread Level (individual calculations). WGSL (WebGPU Shading Language) is the shader language.</p>
<h3 id="real-time-terrain-sculpting-pipeline" tabindex="-1">Real-Time Terrain Sculpting Pipeline <a class="header-anchor" href="#real-time-terrain-sculpting-pipeline" aria-label="Permalink to &quot;Real-Time Terrain Sculpting Pipeline&quot;"></a></h3>
<p>A WebGPU sculpting pipeline would look like this:</p>
<ol>
<li>
<p><strong>Brush application</strong> (compute shader): Update the scalar field values within the brush radius. Each thread handles one voxel. Reads the brush parameters (position, radius, strength, falloff type, operation) from a uniform buffer and applies the modification.</p>
</li>
<li>
<p><strong>Mesh extraction</strong> (compute shader): Run Surface Nets or Marching Cubes on the modified region. The WebGPU SDF Editor by Nijhoff implements this as a multi-stage pipeline: space partitioning across 16,384 cells, octree-based cell splitting, then surface extraction.</p>
</li>
<li>
<p><strong>Vertex buffer update</strong> (GPU-side): Write the extracted vertices directly into a render buffer without roundtripping through CPU memory.</p>
</li>
<li>
<p><strong>Normal computation</strong> (compute shader): Calculate vertex normals from the mesh or from the SDF gradient.</p>
</li>
<li>
<p><strong>Render</strong> (standard pipeline): Draw the mesh with standard PBR materials.</p>
</li>
</ol>
<p>Steps 1-4 can all run on the GPU without any data returning to JavaScript. The CPU only needs to send the brush parameters each frame.</p>
<h3 id="the-webgpu-sdf-editor" tabindex="-1">The WebGPU SDF Editor <a class="header-anchor" href="#the-webgpu-sdf-editor" aria-label="Permalink to &quot;The WebGPU SDF Editor&quot;"></a></h3>
<p>Reinder Nijhoff's WebGPU SDF Editor (January 2026) demonstrates this approach running in Chrome. It supports six primitives (cone, cylinder, capsule, torus, box, sphere), three blend operations (union, subtraction, intersection) with configurable smooth blending, and hierarchical scene graphs. Each primitive occupies 112 bytes in a single GPU buffer.</p>
<p>The rendering pipeline uses 1,024 shadow maps for ambient occlusion and temporal anti-aliasing. This runs at interactive framerates on high-end GPUs.</p>
<h2 id="heightmap-sculpting-the-simpler-path" tabindex="-1">Heightmap Sculpting: The Simpler Path <a class="header-anchor" href="#heightmap-sculpting-the-simpler-path" aria-label="Permalink to &quot;Heightmap Sculpting: The Simpler Path&quot;"></a></h2>
<p>If cave and overhang support isn't required, heightmap sculpting avoids the entire volumetric pipeline. This is how most shipped games handle terrain editing.</p>
<h3 id="runtime-implementation-pattern" tabindex="-1">Runtime Implementation Pattern <a class="header-anchor" href="#runtime-implementation-pattern" aria-label="Permalink to &quot;Runtime Implementation Pattern&quot;"></a></h3>
<p>Unity Runtime Terrain (JohannHotzel, January 2026) demonstrates the standard pattern:</p>
<ol>
<li><strong>Raycast</strong> from the camera through the mouse position to find the terrain hit point.</li>
<li><strong>Map the hit point</strong> to heightmap coordinates.</li>
<li><strong>Apply the brush</strong> to nearby heightmap values, weighted by falloff.</li>
<li><strong>Update the mesh</strong> by setting vertex Y positions from the modified heightmap.</li>
<li><strong>Rebuild the physics collider</strong> to match the new mesh.</li>
</ol>
<p>For our Three.js terrain, steps 1-4 map directly onto the existing architecture. The <code>Chunk</code> class already stores heightmaps and builds meshes from them. Adding sculpting would mean:</p>
<ul>
<li>A raycasting system against chunk meshes (Three.js <code>Raycaster</code>)</li>
<li>Brush application functions that modify <code>chunk.heightmap</code> values</li>
<li>Mesh vertex updates (set Y positions, recalculate normals)</li>
<li>Splat map recalculation for the affected region (so texture blending reflects the new slope/height)</li>
<li>Network broadcast of the edit (brush position, radius, strength, operation) to other clients via the existing WebSocket protocol</li>
</ul>
<h3 id="the-clipmap-approach" tabindex="-1">The Clipmap Approach <a class="header-anchor" href="#the-clipmap-approach" aria-label="Permalink to &quot;The Clipmap Approach&quot;"></a></h3>
<p>Landow.dev describes a &quot;wandering clipmap&quot; for heightmap terrain: a single mesh with variable subdivision density that follows the player. Instead of chunking the heightmap into separate meshes with different LOD levels (what we do now), the clipmap is a continuous mesh that's dense near the camera and coarse at the edges.</p>
<p>This eliminates LOD stitching entirely. The mesh simply has more triangles where you need them and fewer where you don't. The tradeoff is that sculpting requires updating a single large mesh rather than individual chunks, which can be expensive for large edits.</p>
<h3 id="non-destructive-sdf-heightmaps" tabindex="-1">Non-Destructive SDF Heightmaps <a class="header-anchor" href="#non-destructive-sdf-heightmaps" aria-label="Permalink to &quot;Non-Destructive SDF Heightmaps&quot;"></a></h3>
<p>Landow.dev also describes a technique where the heightmap itself is generated from an SDF composition. Shape instances (spheres, boxes, noise functions) are composed in a compute shader using CSG operations, and the output is sampled as a heightmap. This provides non-destructive editing (you can move or delete any shape instance at any time) while keeping the simplicity of heightmap rendering.</p>
<p>This is a compelling hybrid: the data representation is volumetric (SDF CSG tree), but the rendering path is a standard heightmap mesh. You get undo/redo and network-friendly edit operations from the SDF side, and simple rendering and physics from the heightmap side. The limitation remains: no caves or overhangs.</p>
<h2 id="sparse-voxel-octrees-for-large-worlds" tabindex="-1">Sparse Voxel Octrees for Large Worlds <a class="header-anchor" href="#sparse-voxel-octrees-for-large-worlds" aria-label="Permalink to &quot;Sparse Voxel Octrees for Large Worlds&quot;"></a></h2>
<p>Dense 3D grids don't scale. A world that's 1 km on each side at 0.5m resolution would need 8 billion voxels. Sparse Voxel Octrees (SVOs) solve this by recursively subdividing space and only allocating storage for octants that contain the surface boundary.</p>
<p>An SVO naturally provides hierarchical LOD: the tree depth at any point determines the effective resolution. Near the player, the tree is fully expanded (maximum detail). Far away, it's truncated at a coarser level.</p>
<p>For rendering, SVOs can be ray-traced directly (no mesh extraction needed). A GPU ray marcher intersects rays with axis-aligned boxes at each tree level, skipping empty subtrees entirely. This eliminates overdraw problems of chunk-based rendering and avoids greedy meshing artifacts.</p>
<p>The Vulkan-based SVO builder by AdamYuan demonstrates significant performance: 19ms build time for Crytek Sponza at 2^10 resolution on a GTX 1660 Ti.</p>
<p>For sculpting, SVO modification is efficient: only the leaf nodes within the brush radius need to be updated, and the tree structure handles varying resolution naturally. Adding detail where the player sculpts (splitting nodes to higher resolution) and removing detail where they smooth (merging nodes to lower resolution) falls out of the data structure.</p>
<p>The challenge for browser deployment is that WebGL doesn't support compute shaders, and while WebGPU does, the SVO construction and traversal algorithms are complex to implement in WGSL.</p>
<h2 id="network-synchronization-for-multiplayer-sculpting" tabindex="-1">Network Synchronization for Multiplayer Sculpting <a class="header-anchor" href="#network-synchronization-for-multiplayer-sculpting" aria-label="Permalink to &quot;Network Synchronization for Multiplayer Sculpting&quot;"></a></h2>
<p>Our world already has multiplayer via Cloudflare Durable Objects (<code>world-chunk-do.ts</code>). Adding sculpting means synchronizing terrain modifications across all connected clients.</p>
<h3 id="delta-compression" tabindex="-1">Delta Compression <a class="header-anchor" href="#delta-compression" aria-label="Permalink to &quot;Delta Compression&quot;"></a></h3>
<p>Sending raw voxel data is expensive. A 2024 study from Oulu University achieved 2-8x payload improvement by combining delta-encoding with DEFLATE compression, packing voxel updates to less than one byte per voxel. The SDEC codec demonstrates bit-packed delta encoding producing 259-byte average packets versus 1114 bytes with generic serialization.</p>
<h3 id="operation-based-sync-recommended" tabindex="-1">Operation-Based Sync (Recommended) <a class="header-anchor" href="#operation-based-sync-recommended" aria-label="Permalink to &quot;Operation-Based Sync (Recommended)&quot;"></a></h3>
<p>Instead of synchronizing field state, synchronize operations. Each sculpting action becomes a message:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TerrainEditMsg</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  t</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MsgType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">TerrainEdit</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  brush</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    position</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    radius</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    strength</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    falloff</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'smooth'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'linear'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'sharp'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'constant'</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    operation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'raise'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'lower'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'smooth'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'flatten'</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'noise'</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    targetHeight</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>The server broadcasts this to all clients, and each client applies the same deterministic brush operation to its local terrain data. This is the same approach Teardown uses for structural destruction: deterministic commands on a reliable stream.</p>
<p>ALICE-SDF's CSG tree diff/patch takes this further: instead of individual brush strokes, the diff represents the structural change to the entire CSG tree. This enables efficient undo/redo across the network (send the inverse patch) and late-joining clients can reconstruct the full world state by replaying the operation log.</p>
<h3 id="priority-and-throttling" tabindex="-1">Priority and Throttling <a class="header-anchor" href="#priority-and-throttling" aria-label="Permalink to &quot;Priority and Throttling&quot;"></a></h3>
<p>Terrain edits near connected players should be high-priority (immediate broadcast). Edits far from any player can be batched and sent at lower frequency. Enshrouded's voxel networking uses this pattern: 60Hz updates for player-proximate terrain, 10Hz for background regions.</p>
<p>ZSTD compression on the wire reduces packet sizes by up to 60% for terrain update messages.</p>
<h2 id="collaborative-sculpting-concurrent-edits" tabindex="-1">Collaborative Sculpting: Concurrent Edits <a class="header-anchor" href="#collaborative-sculpting-concurrent-edits" aria-label="Permalink to &quot;Collaborative Sculpting: Concurrent Edits&quot;"></a></h2>
<p>When multiple players sculpt the same region simultaneously, you need conflict resolution. <code>cSculpt</code> (CNR Visual Computing Lab, 2016) solved this with a multiresolution merge algorithm. Each edit is represented at multiple scales, and concurrent overlapping edits are merged by blending their multiresolution representations.</p>
<p>For our purposes, a simpler approach works: last-write-wins with server ordering. The Durable Object timestamps each edit and broadcasts them in order. All clients apply edits in the same sequence. Since brush strokes are small, localized, and additive/subtractive, the visual result of slightly reordered concurrent edits is usually indistinguishable from the &quot;correct&quot; order.</p>
<h2 id="inst-sculpt-neural-sdf-editing-research-frontier" tabindex="-1">INST-Sculpt: Neural SDF Editing (Research Frontier) <a class="header-anchor" href="#inst-sculpt-neural-sdf-editing-research-frontier" aria-label="Permalink to &quot;INST-Sculpt: Neural SDF Editing (Research Frontier)&quot;"></a></h2>
<p>INST-Sculpt (arxiv 2502.02891, February 2025) enables stroke-based editing of neural SDFs. Users draw strokes on the surface, and the system deforms the underlying neural field along tubular neighborhoods around the stroke path. Custom brush profiles (configurable cross-sections) control the shape of the deformation.</p>
<p>This is interesting for AI-generated terrain: if the base world is represented as a neural SDF (a small neural network that maps 3D coordinates to signed distance), sculpting modifies the network weights rather than explicit voxel data. The representation is extremely compact (a few MB for an entire world) but evaluation is more expensive than a lookup table.</p>
<p>This is research-stage technology. The inference cost of neural SDFs on consumer hardware is too high for real-time game use today. But it's worth watching, especially as WebGPU shader capabilities improve and model inference gets faster.</p>
<h2 id="world-creator-2026-3-commercial-terrain-state-of-the-art" tabindex="-1">World Creator 2026.3: Commercial Terrain State of the Art <a class="header-anchor" href="#world-creator-2026-3-commercial-terrain-state-of-the-art" aria-label="Permalink to &quot;World Creator 2026.3: Commercial Terrain State of the Art&quot;"></a></h2>
<p>World Creator (BiteTheBytes, March 2026) represents the commercial state of the art for terrain authoring tools. Version 2026.3 added GPU-based terrain generation with automatic terrain adaptation (terrain conforms to placed objects), camera-focused object distribution for LOD optimization, and real-world elevation data import (GeoTIFF, HGT, DTED).</p>
<p>Since then, World Creator 2026.4 (April 28, 2026) added mathematical expressions in numerical fields, terrain normal blending to seat hero objects into the surface, full decal support, and VRAM scaling that adjusts the maximum object count to available GPU memory. BiteTheBytes also shipped a free Community Edition that's feature-complete but export-disabled, effectively an unlimited trial.</p>
<p>Their approach uses GPU compute for all terrain operations: erosion simulation, river carving, texture painting. The brush tools are GPU-accelerated with real-time viewport feedback. This matches the WebGPU compute pipeline described above, running on desktop GPUs.</p>
<h2 id="what-we-ve-already-built-24-spikes-and-a-production-world" tabindex="-1">What We've Already Built: 24 Spikes and a Production World <a class="header-anchor" href="#what-we-ve-already-built-24-spikes-and-a-production-world" aria-label="Permalink to &quot;What We've Already Built: 24 Spikes and a Production World&quot;"></a></h2>
<p>The <code>world/spikes/</code> directory contains 24 self-contained prototypes. These aren't toy demos. They're a progressive R&amp;D pipeline where each spike solved a specific problem, benchmarked it against a target, and informed the next one. The sculpting system builds on all of them, not just the later volumetric ones.</p>
<h3 id="the-production-heightmap-terrain-world-client" tabindex="-1">The Production Heightmap Terrain (<code>world/client/</code>) <a class="header-anchor" href="#the-production-heightmap-terrain-world-client" aria-label="Permalink to &quot;The Production Heightmap Terrain (`world/client/`)&quot;"></a></h3>
<p>The live world uses a chunked heightmap system in Three.js WebGL:</p>
<ul>
<li><code>noise.ts</code> generates terrain height via FBM value noise (5 octaves for hills, 4 for ridges, 3 for micro-detail) with a deterministic <code>terrainHeight(wx, wz)</code> function</li>
<li><code>chunk.ts</code> builds <code>PlaneGeometry</code> meshes from <code>Float32Array</code> heightmaps with 3 LOD levels (32/8/4 segments per 64-unit chunk), and places instanced trees/billboards with seeded random placement and per-object colliders</li>
<li><code>chunk-manager.ts</code> streams chunks in rings around the player (radius 1 at LOD0, radius 3 at LOD1, radius 6 at LOD2) with edge stitching via linear interpolation in <code>stitchEdge()</code>, and provides <code>getHeight()</code>, <code>getNormal()</code>, and <code>resolveCollisions()</code> for the physics layer</li>
<li><code>terrain-material.ts</code> does 4-layer splat-based texture blending (grass/rock/sand/dirt) via <code>MeshStandardMaterial.onBeforeCompile</code> with slope and height-driven weights, plus per-layer normal map blending</li>
<li><code>character-controller.ts</code> samples terrain height every frame for gravity, grounding, and slope rejection (max slope cos 50 degrees). Sculpting must feed modified heights into this system instantly or the player falls through edited terrain</li>
<li><code>placement.ts</code> already has a <code>Raycaster</code> hitting chunk meshes for the object placement tool. The brush tool should follow this exact pattern rather than building raycasting from scratch</li>
<li><code>protocol.ts</code> defines MessagePack-encoded messages for multiplayer sync via <code>world-chunk-do.ts</code> Durable Object, which currently handles <code>PlayerState</code>, <code>PlaceObject</code>, <code>RemoveObject</code>, and <code>Snapshot</code> messages. Terrain edits will need a new message type</li>
<li><code>world-chunk-do.ts</code> (Cloudflare Worker) persists placed objects to Durable Object storage and broadcasts to connected players at 50ms intervals. It has no concept of terrain modifications yet</li>
</ul>
<h3 id="spikes-01-11-the-foundation-layer" tabindex="-1">Spikes 01-11: The Foundation Layer <a class="header-anchor" href="#spikes-01-11-the-foundation-layer" aria-label="Permalink to &quot;Spikes 01-11: The Foundation Layer&quot;"></a></h3>
<p>These spikes validated the core systems that sculpting will depend on. Skipping them means missing constraints the sculpting system must respect.</p>
<p><strong>Spike 01 (Terrain + Instancing):</strong> The first terrain prototype in Three.js. Established the PlaneGeometry + heightmap pattern and instanced object placement that <code>chunk.ts</code> still uses.</p>
<p><strong>Spike 02 (Rapier Physics Worker):</strong> Rapier 3D running in a Web Worker with a <code>ColliderDesc.heightfield()</code> collider. Built a kinematic character controller with autostep, slope limits, and snap-to-ground. This spike proved that physics can run off the main thread against a heightfield. If we sculpt the terrain, the physics heightfield must be rebuilt or replaced with a trimesh collider for MC chunks.</p>
<p><strong>Spike 05 (LLM Behaviors):</strong> Not directly terrain-related but established the JSON behavior schema for game objects. Relevant because sculpted terrain features could trigger behaviors (e.g., a carved river spawns water effects).</p>
<p><strong>Spike 06 (Chunk Streaming):</strong> First chunk load/swap system with dynamic loading as the player moves. Established the pattern that <code>chunk-manager.ts</code> uses: colored regions that load and unload. Sculpting must preserve edit state when chunks are unloaded and reloaded.</p>
<p><strong>Spike 07 (GPU Vegetation from Density Maps):</strong> Instanced grass and trees placed via density maps that sample terrain height and slope. Sculpting invalidates vegetation placement: if the terrain height changes, trees may end up floating or buried. The density map must be regenerated for edited chunks.</p>
<p><strong>Spike 08 (Terrain Material Shader Cost):</strong> Benchmarked triplanar projection, normal maps, and 4-layer blending. Measured exact ms cost of each feature. Found that triplanar + normals + 4 layers stays within budget at 45+ FPS. This budget matters for sculpted terrain: if we add a 5th layer for &quot;edited soil&quot; or change the blending for carved surfaces, we know exactly how much headroom exists.</p>
<p><strong>Spike 09 (CSM Shadows Budget):</strong> Cascaded shadow maps with 3 cascades at 1024^2 resolution. Measured shadow cost at ~1.5ms. Sculpted terrain changes shadow maps, but the cost is constant regardless of terrain shape.</p>
<p><strong>Spike 10 (Geometry Clipmaps + Geomorphing):</strong> Nested clipmap rings with geomorphing between LOD levels to eliminate pop-in. The constant triangle count means predictable GPU cost. Geomorphing matters for sculpting: when the player sculpts near an LOD boundary, the morph between LOD levels must reflect the edit. If the edit only exists in the high-res ring, the geomorph target is wrong.</p>
<p><strong>Spike 11 (Heightmap Chunk Streaming):</strong> More advanced chunk streaming with a visual grid showing loaded/loading/unloaded states per LOD level. Established the streaming budget: max chunks loaded per frame, prioritization of chunks that need LOD upgrades. Sculpting adds a new priority signal: chunks the player is actively editing should never be unloaded.</p>
<h3 id="spikes-12-14-webgpu-three-js-integration" tabindex="-1">Spikes 12-14: WebGPU + Three.js Integration <a class="header-anchor" href="#spikes-12-14-webgpu-three-js-integration" aria-label="Permalink to &quot;Spikes 12-14: WebGPU + Three.js Integration&quot;"></a></h3>
<p><strong>Spike 12 (WebGPU Marching Cubes):</strong> The first volumetric spike. Four 64^3 SDF chunks with animated sphere caves, running entirely on the GPU. Uses raw WebGPU: compute pipelines for SDF evaluation, MC extraction with the Twinklebear case table (256 configurations, 16 entries each), atomic vertex counter, and indirect draw. Performance target was &lt;4ms per chunk, &lt;12ms for all 4. This validated that GPU MC is fast enough for real-time re-meshing in the browser. Every subsequent volumetric spike reuses the MC case table and WGSL shaders defined here.</p>
<p><strong>Spike 13 (Reset Baseline from Spike 12):</strong> Ported Spike 12's raw WebGPU draw path to run inside Three.js's <code>WebGPURenderer</code>, accessing the backend's <code>device</code> directly. The render pipeline still uses raw WebGPU (<code>drawIndirect</code> with struct Vertex vec4+vec4). This proved that custom compute and Three.js scene rendering can coexist on the same GPU device.</p>
<p><strong>Spike 14 (Three.js WebGPU Incremental Hardening):</strong> Replaced the raw render pipeline with Three.js <code>StorageBufferAttribute</code> for positions and normals. MC compute writes directly into these GPU-resident buffers. The <code>drawIndirect</code> buffer controls how many vertices Three.js draws. This is the pattern all subsequent spikes use: compute stays as raw WebGPU, rendering goes through Three.js scene graph. The Three.js version across these spikes progressed from 0.170.0 to 0.172.0 as the WebGPU backend stabilized.</p>
<h3 id="spike-15-17-transvoxel-lod-stitching" tabindex="-1">Spike 15-17: Transvoxel LOD Stitching <a class="header-anchor" href="#spike-15-17-transvoxel-lod-stitching" aria-label="Permalink to &quot;Spike 15-17: Transvoxel LOD Stitching&quot;"></a></h3>
<p><strong>Spike 15 (Transvoxel Seam Scaffold):</strong> Added the three-zone architecture: MC chunk (volumetric center), transition strip (seam between MC boundary and heightmap), and terrain ring (surrounding heightmap). All three share a material pass. The transition strip is a placeholder mesh at this stage, not real Transvoxel cells.</p>
<p><strong>Spike 16 (Transvoxel +X Face with Shared Heightmap):</strong> Two critical breakthroughs in one spike. First, replaced the flat terrain plane in the SDF with a shared Perlin heightmap: a 257x257 Float32Array uploaded to the GPU as a storage buffer, sampled via bilinear interpolation in the SDF compute shader. The MC surface and the heightmap mesh now agree on the same ground truth. Second, implemented real Transvoxel transition cells for the +X face by fetching Eric Lengyel's reference data tables from GitHub (<code>transitionCellClass</code>, <code>transitionVertexData</code>, <code>transitionCellData</code>) and the npm <code>transvoxel-data</code> package. The CPU evaluates 9-sample transition cells (512 configurations, 73 equivalence classes), places vertices by interpolating SDF values at grid points, and handles winding inversion for mirrored cases.</p>
<p><strong>Spike 17 (Dual MC 1x/2x LOD):</strong> Two MC chunks side by side at different resolutions. High-res: 62 cells with cell_scale=1.0. Low-res: 31 cells with cell_scale=2.0. The MC shader gained <code>cell_scale</code> and <code>grid_points</code> uniforms. Introduced <code>transition_shrink</code>: the low-res chunk's face-0 boundary vertices are pulled inward by 15% of cell_scale, creating a thin gap that Transvoxel transition cells fill without z-fighting. This is the LOD model the production system needs: close chunks at full resolution, far chunks at half resolution, Transvoxel at every boundary.</p>
<h3 id="spikes-18-21-transvoxel-corner-cases-and-gpu-acceleration" tabindex="-1">Spikes 18-21: Transvoxel Corner Cases and GPU Acceleration <a class="header-anchor" href="#spikes-18-21-transvoxel-corner-cases-and-gpu-acceleration" aria-label="Permalink to &quot;Spikes 18-21: Transvoxel Corner Cases and GPU Acceleration&quot;"></a></h3>
<p>These four spikes each solved a specific failure case in the Transvoxel implementation. Grouping them obscures the distinct problems.</p>
<p><strong>Spike 18 (Heightmap 2:1 Seam):</strong> Applied Transvoxel to a pure heightmap boundary where one side is twice the resolution of the other. No MC involved. The seam between a 62-cell and 31-cell heightmap chunk is generated from Transvoxel transition tables with 15% low-face shrink. This validated that Transvoxel works for the heightmap-only case, not just MC.</p>
<p><strong>Spike 19 (64/32/32/16 Corner Grid):</strong> The hardest stitching case: four chunks of different resolutions meeting at a corner point (64, 32, 32, and 16 cells). The seam system must generate transition cells along four edges (A-B, A-C, B-D, C-D) with correct winding for each direction. This spike proved that the Transvoxel tables handle the multi-resolution corner without custom-case logic.</p>
<p><strong>Spike 20 (GPU Transvoxel Corner):</strong> Moved the Transvoxel transition cell generation to the GPU for the 64/32/32/16 corner layout. The CPU was a bottleneck when regenerating transition cells every frame for animated terrain. GPU compute generates the seam vertices in the same pass as the MC extraction.</p>
<p><strong>Spike 21 (GPU MC + Transvoxel Corner):</strong> Combined full GPU MC extraction with GPU Transvoxel seam generation in a single compute dispatch sequence. Both MC chunks and all four seams are generated on the GPU, with vertex counts managed via atomic counters and drawn with <code>drawIndirect</code>. This is the complete GPU pipeline for multi-resolution volumetric terrain with seamless LOD transitions.</p>
<h3 id="spikes-22-24-the-hybrid-architecture" tabindex="-1">Spikes 22-24: The Hybrid Architecture <a class="header-anchor" href="#spikes-22-24-the-hybrid-architecture" aria-label="Permalink to &quot;Spikes 22-24: The Hybrid Architecture&quot;"></a></h3>
<p><strong>Spike 22 (Hybrid MC/Heightmap Policy):</strong> The key architecture spike. Chunks are heightmaps by default. When the animated deformation sphere intersects a chunk's AABB, that chunk switches to MC mode. The rest stay as static heightmap meshes. Layout: 64, 32/32, and 16-cell chunks at different resolutions. Transvoxel seams handle every boundary, including MC-to-heightmap transitions. The spike tracks MC chunk count vs HM chunk count and vertex overflow per frame.</p>
<p><strong>Spike 23 (Policy-Driven Chunk Modes):</strong> Loaded as a patch on top of Spike 22. Added camera-distance hysteresis (chunks don't flicker between modes when the camera is near a threshold) and an edit mask (chunks that have been deformed stay in MC mode even if the deformation source moves away). This is the &quot;sticky edit&quot; behavior sculpting needs: once a player carves a cave, that chunk stays volumetric forever.</p>
<p><strong>Spike 24 (Policy + Clipmap Rings):</strong> The most advanced spike. Combines the near-field policy system from Spike 23 with far-field geometry clipmap rings from Spike 10. Upgraded to Three.js 0.183.1. Near-field uses HM/MC hybrid with Transvoxel seams at 64/32/16 resolution. Far-field uses static-center clipmap rings that follow the camera. This is the complete terrain rendering architecture: chunked volumetric sculpting where needed, cheap clipmap terrain everywhere else.</p>
<h3 id="why-marching-cubes-not-surface-nets" tabindex="-1">Why Marching Cubes, Not Surface Nets <a class="header-anchor" href="#why-marching-cubes-not-surface-nets" aria-label="Permalink to &quot;Why Marching Cubes, Not Surface Nets&quot;"></a></h3>
<p>The external research section of this guide suggests Surface Nets as the strongest candidate for browser terrain sculpting. But every spike in the pipeline uses Marching Cubes. That's not an accident.</p>
<p>MC's defining advantage is embarrassing parallelism: each cube is completely independent. The WGSL compute shaders in Spikes 12-24 dispatch one thread per cube with zero inter-cell communication. Atomic counters handle vertex allocation. This maps perfectly to GPU workgroups.</p>
<p>Surface Nets places one vertex per surface-containing cell, then connects neighbors. That neighbor connectivity is an inter-cell dependency. The <code>fast-surface-nets</code> crate handles it on the CPU with careful iteration order. On the GPU, it requires either a two-pass approach (find vertices, then connect) or shared memory within workgroups. Both are possible in WebGPU but add complexity.</p>
<p>The practical recommendation: stay with Marching Cubes for the sculpting pipeline. It's proven in our codebase, the WGSL shaders exist and are benchmarked, and the Transvoxel seam system is built around MC's edge-based vertex placement. Surface Nets is worth revisiting if MC's aliasing on binary data becomes a visible problem, but for SDF terrain where values are smooth gradients, MC produces clean results.</p>
<h2 id="practical-architecture-for-sculpting" tabindex="-1">Practical Architecture for Sculpting <a class="header-anchor" href="#practical-architecture-for-sculpting" aria-label="Permalink to &quot;Practical Architecture for Sculpting&quot;"></a></h2>
<p>The spike sequence solved the rendering pipeline. What remains is the brush system, cascading side effects through the game systems, and multiplayer synchronization. Here's the plan, building on every spike.</p>
<h3 id="phase-1-heightmap-sculpting-minimal-change-maximum-reach" tabindex="-1">Phase 1: Heightmap Sculpting (Minimal Change, Maximum Reach) <a class="header-anchor" href="#phase-1-heightmap-sculpting-minimal-change-maximum-reach" aria-label="Permalink to &quot;Phase 1: Heightmap Sculpting (Minimal Change, Maximum Reach)&quot;"></a></h3>
<p>Add brush tools that modify chunk heightmaps in the production <code>world/client/</code> code. This works within the existing WebGL renderer and doesn't require WebGPU.</p>
<p><strong>Brush input:</strong> Follow the <code>PlacementTool</code> pattern in <code>placement.ts</code>. It already has a <code>Raycaster</code> hitting <code>chunkManager.getChunkMeshes()</code> and tracking a ghost mesh at the hit point. A <code>TerrainBrushTool</code> would do the same raycast but modify the chunk heightmap instead of placing an object. The <code>World.onMouseDown</code> handler already dispatches based on tool state.</p>
<p><strong>Chunk modification (<code>Chunk.applyBrush</code>):</strong> Map the brush world position to heightmap grid coordinates. For each grid point within the brush radius, compute the falloff-weighted displacement and add/subtract from the heightmap value. Then update the mesh: set vertex Y positions from the modified heightmap, recompute normals via central differencing (the same <code>terrainHeight(wx +/- eps, wz)</code> pattern already used in <code>chunk.ts</code> line 155-158), and regenerate the splat map via <code>createSplatMap()</code> in <code>terrain-material.ts</code> for the affected region so slope-driven texture blending updates.</p>
<p><strong>Character controller:</strong> <code>CharacterController.update()</code> calls <code>getHeight()</code> every frame for grounding. <code>ChunkManager.getHeight()</code> delegates to <code>Chunk.sampleHeight()</code>, which reads from the chunk's <code>heightmap</code> Float32Array. Since we're modifying that array directly, the character controller picks up the change on the next frame with zero additional wiring.</p>
<p><strong>Object invalidation:</strong> Tree instances in <code>chunk.ts</code> are placed by sampling <code>terrainHeight()</code> at spawn time. After sculpting, trees in the affected area may be at the wrong height. Phase 1 can defer this (trees float slightly on small edits). Phase 2 needs a <code>chunk.invalidateObjects()</code> that re-samples heights and rebuilds the instance matrices. Same for the colliders used in <code>resolveCollisions()</code>.</p>
<p><strong>Rapier physics (if integrated):</strong> Spike 02 proved heightfield colliders work. If Rapier is active, the heightfield collider must be rebuilt or patched for the modified chunk. Rapier's <code>ColliderDesc.heightfield()</code> takes a flat Float32Array, so it's a straight replacement.</p>
<p><strong>Network sync:</strong> Add <code>MsgType.TerrainEdit = 10</code> to <code>protocol.ts</code>:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TerrainEditMsg</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  t</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> MsgType</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">TerrainEdit</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  cx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  cz</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  brush</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    wx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    wz</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    radius</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    strength</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    falloff</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">    operation</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>The <code>WorldChunkDO</code> broadcasts this to all clients and appends it to a per-chunk edit log stored in Durable Object storage. Late-joining clients receive the edit log in the <code>Snapshot</code> message and replay it to reconstruct the terrain state. All clients apply the same deterministic brush function, so they converge on the same heightmap.</p>
<p><strong>Chunk unload/reload:</strong> Spike 06 and Spike 11 established the streaming pattern. When a chunk is unloaded and later reloaded, the edit log for that chunk must be replayed against the base procedural heightmap. The edit log is stored server-side (Durable Object) and included in the Snapshot message.</p>
<h3 id="phase-2-volumetric-sculpting-with-the-spike-22-24-architecture" tabindex="-1">Phase 2: Volumetric Sculpting with the Spike 22-24 Architecture <a class="header-anchor" href="#phase-2-volumetric-sculpting-with-the-spike-22-24-architecture" aria-label="Permalink to &quot;Phase 2: Volumetric Sculpting with the Spike 22-24 Architecture&quot;"></a></h3>
<p>Port the Spike 24 pipeline into the production world. When a player sculpts below the surface (carving a cave, digging a tunnel), the affected chunk transitions from heightmap mode to MC mode.</p>
<p><strong>WebGPU renderer migration:</strong> Spikes 13-14 proved that Three.js's <code>WebGPURenderer</code> can host custom compute alongside the scene graph. The production world moves from <code>WebGLRenderer</code> to <code>WebGPURenderer</code> with <code>StorageBufferAttribute</code> for MC chunks. Fallback to the Phase 1 heightmap-only path when WebGPU is unavailable.</p>
<p><strong>Per-chunk SDF allocation:</strong> Follow Spike 22's hybrid pattern. Each chunk starts as a heightmap. On the first volumetric brush stroke, allocate a 64^3 Float32Array, initialize it by sampling the heightmap (the SDF value at each point is <code>world.y - heightmap_value</code>), and switch to MC rendering. The policy system from Spike 23 ensures the chunk stays in MC mode permanently (the &quot;sticky edit&quot; behavior from the edit mask).</p>
<p><strong>Shared heightmap in the SDF shader:</strong> Spike 16's <code>height_at()</code> function. Upload the chunk's heightmap to a GPU storage buffer. The SDF compute shader evaluates <code>max(height_sdf, edit_sdf)</code> where <code>height_sdf = world.y - height_at(world.xz)</code> and <code>edit_sdf</code> contains the brush modifications. MC and heightmap chunks agree on ground truth at their boundaries.</p>
<p><strong>Transvoxel seams:</strong> The full stack from Spikes 15-21. MC-to-heightmap boundaries use transition cells with the shrink gap. MC-to-MC boundaries at different resolutions use Spike 17's dual-LOD pattern. The corner case from Spike 19 handles 4-way intersections. Spike 21's GPU compute generates all seam geometry in the same dispatch.</p>
<p><strong>Clipmap far-field:</strong> Spike 24's clipmap rings for terrain outside the sculpting range. Sculpting never touches these rings; they sample the base procedural heightmap.</p>
<p><strong>Geomorphing:</strong> Spike 10's geomorphing eliminates pop-in at LOD transitions. For edited chunks, the geomorph target must include the edit. If a chunk is MC at LOD0 and its LOD1 neighbor is a heightmap, the geomorph blends between the two representations. This requires sampling the edit log even at lower LODs.</p>
<p><strong>Material budget:</strong> Spike 08 benchmarked 4-layer triplanar + normals at 45+ FPS. MC chunks need the same material. The splat map can be generated from the SDF gradient (steep = rock, flat = grass) instead of the heightmap slope. This stays within the 4-layer budget.</p>
<p><strong>Vegetation invalidation:</strong> Spike 07's density-map vegetation depends on terrain height and slope. When a chunk transitions to MC mode, tree instances must be regenerated by sampling the SDF surface. Trees on overhangs or inside caves must be culled. The instanced mesh matrices from <code>chunk.ts</code> are rebuilt from the new surface.</p>
<h3 id="phase-3-csg-edit-trees-for-undo-redo-and-network-sync" tabindex="-1">Phase 3: CSG Edit Trees for Undo/Redo and Network Sync <a class="header-anchor" href="#phase-3-csg-edit-trees-for-undo-redo-and-network-sync" aria-label="Permalink to &quot;Phase 3: CSG Edit Trees for Undo/Redo and Network Sync&quot;"></a></h3>
<p>Replace raw SDF mutation with a CSG operation tree. Each brush stroke appends a primitive (sphere, capsule, box) with an operation (add, subtract, smooth blend). The SDF is recomputed from the tree.</p>
<p><strong>Benefits:</strong></p>
<ul>
<li>Non-destructive: any edit can be removed from the tree to undo it</li>
<li>Network-efficient: broadcast the CSG operation, not the raw field values</li>
<li>Deterministic: all clients build the same SDF from the same operation sequence</li>
<li>ALICE-SDF's CSG tree diff/patch provides bandwidth-efficient synchronization and undo/redo across the network</li>
</ul>
<p><strong>Durable Object storage:</strong> The edit tree per chunk replaces the flat edit log from Phase 1. The <code>WorldChunkDO</code> stores the CSG tree structure, not raw heightmap deltas. <code>Snapshot</code> messages include the tree, and late-joining clients evaluate it to produce the local SDF.</p>
<h3 id="phase-4-collaborative-sculpting" tabindex="-1">Phase 4: Collaborative Sculpting <a class="header-anchor" href="#phase-4-collaborative-sculpting" aria-label="Permalink to &quot;Phase 4: Collaborative Sculpting&quot;"></a></h3>
<p>Add concurrent edit support with server-ordered operation replay. The Durable Object timestamps each edit and broadcasts in order. Late-joining clients receive the operation log and reconstruct the world state. The existing <code>Snapshot</code> message type extends to include terrain edit history per chunk.</p>
<p>Since brush strokes are small, localized, and additive/subtractive, the visual result of slightly reordered concurrent edits is usually indistinguishable from the &quot;correct&quot; order. Last-write-wins with server ordering is sufficient. The Durable Object's <code>tick()</code> function (currently running at 50ms intervals for player state) adds terrain edit broadcasts to the same loop.</p>
<h2 id="key-references" tabindex="-1">Key References <a class="header-anchor" href="#key-references" aria-label="Permalink to &quot;Key References&quot;"></a></h2>
<p><strong>Algorithms:</strong></p>
<ul>
<li>Lorensen &amp; Cline, &quot;Marching Cubes&quot; (1987)</li>
<li>Eric Lengyel, &quot;Transvoxel Algorithm&quot; (2009), transvoxel.org</li>
<li>Losasso &amp; Hoppe, &quot;Geometry Clipmaps&quot; (SIGGRAPH 2004)</li>
<li>&quot;A High-Performance SurfaceNets Discrete Isocontouring Algorithm&quot; (arxiv 2401.14906, 2024)</li>
<li>MCHex (arxiv 2511.02064, 2025)</li>
</ul>
<p><strong>Implementations:</strong></p>
<ul>
<li>bevy-sculpter v0.18.0 (Rust, Surface Nets + SDF brushes)</li>
<li>fast-surface-nets (Rust, 20M tri/sec)</li>
<li>ALICE-SDF v1.3.0 (Rust + WASM, CSG tree diff/patch)</li>
<li>WebGPU SDF Editor (Nijhoff, January 2026)</li>
<li>SculptingPro (Unity runtime sculpting API)</li>
<li>TerraBrush (Godot terrain sculpting GDExtension)</li>
</ul>
<p><strong>Games:</strong></p>
<ul>
<li>Dreams (Media Molecule, SDF + point cloud rendering, SIGGRAPH 2015)</li>
<li>Teardown (Voxagon, voxel DDA ray tracing, deterministic multiplayer destruction)</li>
<li>Mike Turitzin's SDF engine (brick maps + geometry clipmaps, January 2026)</li>
</ul>
<p><strong>Network:</strong></p>
<ul>
<li>&quot;Optimizing payload size for voxel state synchronization&quot; (Oulu, 2024)</li>
<li>Teardown multiplayer (semi-deterministic destruction sync, March 2026)</li>
<li>cSculpt (collaborative mesh sculpting with multiresolution merge)</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Best AI Video Models with Reference Image Support (2026)]]></title>
            <link>https://app.cinevva.com/guides/long-reference-video-models</link>
            <guid>https://app.cinevva.com/guides/long-reference-video-models</guid>
            <pubDate>Sat, 28 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The AI video generation models that produce long, consistent video from reference images in 2026 - native long-form generators, reference-and-extend workflows, and open-source options you can self-host.]]></description>
            <content:encoded><![CDATA[<h1 id="best-ai-video-models-with-reference-image-support-2026" tabindex="-1">Best AI Video Models with Reference Image Support (2026) <a class="header-anchor" href="#best-ai-video-models-with-reference-image-support-2026" aria-label="Permalink to &quot;Best AI Video Models with Reference Image Support (2026)&quot;"></a></h1>
<p><em>Last updated: June 2026.</em></p>
<p>Generating a 10-second clip is easy now. Every major model does it. The real question is: can you generate 5 or 10 minutes of coherent video where a character looks the same in minute one and minute eight? Where the scene holds together across hundreds of frames?</p>
<p>That's the hard problem. And it's where things are shifting fast. This guide covers every model we've found that either generates long video natively or supports the workflows needed to build long-form content with consistent characters through reference images. We split them into three tiers: models that generate minutes-long video directly, models with strong reference image support that you extend through continuation, and open-source options you can run yourself.</p>
<h2 id="tier-1-native-long-form-generation-minutes" tabindex="-1">Tier 1: Native Long-Form Generation (Minutes+) <a class="header-anchor" href="#tier-1-native-long-form-generation-minutes" aria-label="Permalink to &quot;Tier 1: Native Long-Form Generation (Minutes+)&quot;"></a></h2>
<p>These models generate video measured in minutes, not seconds. They're built from the ground up for temporal consistency over long sequences.</p>
<h3 id="longcat-video" tabindex="-1">LongCat Video <a class="header-anchor" href="#longcat-video" aria-label="Permalink to &quot;LongCat Video&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/YzVLfMO7K5E" title="LongCat-Video: Generate High-Quality Long Videos with AI" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">LongCat Video generates minutes-long coherent video from a single prompt, with no color drift or temporal inconsistency across the full duration.</div>
<p>Meituan released LongCat Video in late 2025. It's a 13.6 billion parameter diffusion transformer and it's the first model that can reliably generate coherent video up to 15 minutes long.</p>
<p>The model supports text-to-video, image-to-video, and video continuation in a unified pipeline. In I2V mode, the input image becomes the literal first frame of the video. It's not a loose character reference you can place in any scene. The model animates forward from that starting frame while using &quot;Cross-Chunk Latent Stitching&quot; to keep referencing the original image throughout generation, preventing color drift and maintaining visual consistency over long sequences. An updated 2026 variant adds audio-driven avatar generation with lip-sync for 5+ minute talking head videos.</p>
<p>Under the hood, LongCat uses a coarse-to-fine generation approach with Block Sparse Attention to handle the massive sequence lengths. RLHF tuning improves motion quality. On the VBench 2.0 benchmark it ranks third overall, behind Google Veo 3 and Shengshu's Vidu Q1.</p>
<p><strong>Availability:</strong> Open source under MIT license. Available through fal.ai API at $0.04 per generated second ($36 for a 15-minute video at 720p). Also available through LongCat's own platform with credit-based pricing.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Max duration</td>
<td>~15 minutes</td>
</tr>
<tr>
<td>Resolution</td>
<td>720p at 30fps</td>
</tr>
<tr>
<td>Parameters</td>
<td>13.6B</td>
</tr>
<tr>
<td>Reference images</td>
<td>First-frame only (I2V mode, not character reference)</td>
</tr>
<tr>
<td>License</td>
<td>MIT</td>
</tr>
<tr>
<td>API cost</td>
<td>~$0.04/second (fal.ai)</td>
</tr>
</tbody>
</table>
<h3 id="seaweed-apt2" tabindex="-1">Seaweed APT2 <a class="header-anchor" href="#seaweed-apt2" aria-label="Permalink to &quot;Seaweed APT2&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/9hDE3HzL7lg" title="Seaweed APT2: Real-Time Controllable AI Video Generation" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Seaweed APT2 generates video autoregressively at 24fps with interactive camera and pose control, closer to a game engine than a render queue.</div>
<p>ByteDance's Seaweed APT2 takes a different approach. Instead of generating a complete video upfront, it produces frames autoregressively at 24fps with just 0.16 seconds latency per frame on a single H100. The result is stable video up to 5 minutes with temporal consistency that holds.</p>
<p>The technical trick is Autoregressive Adversarial Post-Training (AAPT), which converts a pretrained bidirectional video diffusion model into a unidirectional autoregressive generator. Single network forward evaluation per frame. That's what makes real-time generation possible.</p>
<p>What makes this model interesting beyond raw length is interactivity. You can control the camera, animate characters through pose detection, and manipulate scenes while the video renders. Think of it less as &quot;generate a video&quot; and more as &quot;steer a video in real time.&quot;</p>
<p><strong>Availability:</strong> Research phase only. Not publicly available yet. The 7B base model (Seaweed-7B) has a published paper but the APT2 weights haven't been released.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Max duration</td>
<td>~5 minutes</td>
</tr>
<tr>
<td>Resolution</td>
<td>736x416 (single GPU), up to 720p (8 GPUs)</td>
</tr>
<tr>
<td>Parameters</td>
<td>8B</td>
</tr>
<tr>
<td>Reference images</td>
<td>Via I2V and interactive pose control</td>
</tr>
<tr>
<td>License</td>
<td>Not released</td>
</tr>
<tr>
<td>Status</td>
<td>Research preview</td>
</tr>
</tbody>
</table>
<h3 id="helios" tabindex="-1">Helios <a class="header-anchor" href="#helios" aria-label="Permalink to &quot;Helios&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/vd_AgHtOUFQ" title="Helios: Real-Time Long Video Generation" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Helios runs at 19.5 FPS on a single H100, generating minute-scale video while simulating and correcting for temporal drift during training.</div>
<p>Helios comes from Peking University, built on top of Wan 2.1. It's a 14B parameter model that generates minute-scale video at 19.5 FPS on a single H100. The key innovation is how it handles long-video drifting. Instead of using conventional anti-drifting techniques like self-forcing or keyframe sampling, Helios simulates drifting during training so the model learns to correct for it.</p>
<p>It natively supports text-to-video, image-to-video, and video-to-video tasks. The I2V mode accepts reference images to seed the generation.</p>
<p><strong>Availability:</strong> Fully open source under Apache 2.0. Released March 2026. Code and weights on GitHub (PKU-YuanGroup/Helios). Integrated into Diffusers, SGLang, and vLLM-Omni. Gradio demo on HuggingFace Spaces.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Max duration</td>
<td>Minute-scale (no fixed cap)</td>
</tr>
<tr>
<td>Resolution</td>
<td>720p</td>
</tr>
<tr>
<td>Parameters</td>
<td>14B</td>
</tr>
<tr>
<td>Reference images</td>
<td>Yes (I2V mode)</td>
</tr>
<tr>
<td>License</td>
<td>Apache 2.0</td>
</tr>
<tr>
<td>Hardware</td>
<td>Single H100 for real-time</td>
</tr>
</tbody>
</table>
<h3 id="skyreels-v2-v3" tabindex="-1">SkyReels V2 / V3 <a class="header-anchor" href="#skyreels-v2-v3" aria-label="Permalink to &quot;SkyReels V2 / V3&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/NJcRyQu30yM" title="SkyReels AI: Infinite-Length Video Generation" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">SkyReels V3 accepts 1-4 reference images and generates unlimited-length video with multi-shot switching and audio-guided avatar synthesis.</div>
<p>Skywork's SkyReels line aims for infinite-length video. V2 uses an AutoRegressive Diffusion-Forcing architecture that generates video without a fixed duration cap. V3, released January 2026, unifies reference image-to-video, video-to-video extension, and audio-guided avatar generation in a single model.</p>
<p>V3 accepts 1 to 4 reference images and preserves subject identity across the generated video. The video-to-video mode enables seamless single-shot continuation and multi-shot switching with cinematographic transitions.</p>
<p>Skywork shipped SkyReels V4 on February 25, 2026, the first open-source model to generate video and audio together in a single dual-stream pass at up to 1080p/32fps. It accepts text, images, video clips, masks, and audio as conditioning inputs and unifies generation, inpainting, and editing in one framework, extending the V2/V3 reference-and-extend line with native sound. It now sits near the top of the Artificial Analysis text-to-video arena.</p>
<p><strong>Availability:</strong> Fully open source. Models from 1.3B to 14B parameters. Available at 540p and 720p. Code and weights on GitHub and HuggingFace.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Max duration</td>
<td>Unlimited (autoregressive)</td>
</tr>
<tr>
<td>Resolution</td>
<td>540p, 720p</td>
</tr>
<tr>
<td>Parameters</td>
<td>1.3B, 5B, 14B</td>
</tr>
<tr>
<td>Reference images</td>
<td>1-4 images (V3)</td>
</tr>
<tr>
<td>License</td>
<td>Open source</td>
</tr>
<tr>
<td>Hardware</td>
<td>Minimum RTX 4090, recommended 4-8x A100</td>
</tr>
</tbody>
</table>
<h2 id="tier-2-short-clips-with-strong-reference-extension" tabindex="-1">Tier 2: Short Clips with Strong Reference + Extension <a class="header-anchor" href="#tier-2-short-clips-with-strong-reference-extension" aria-label="Permalink to &quot;Tier 2: Short Clips with Strong Reference + Extension&quot;"></a></h2>
<p>These models generate 8-60 second clips but offer strong reference image support and video extension features. For long-form content, you chain clips together using the model's continuation or extension endpoints. Character consistency comes from reference images that persist across generations.</p>
<p>This is the practical workflow most creators use today for content longer than a minute. The quality per-clip is often higher than the native long-form models.</p>
<h3 id="kling-3-0-omni-kuaishou" tabindex="-1">Kling 3.0 Omni (Kuaishou) <a class="header-anchor" href="#kling-3-0-omni-kuaishou" aria-label="Permalink to &quot;Kling 3.0 Omni (Kuaishou)&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/gn3YTrStUn8" title="Introducing Kling 3.0: AI Video Generation" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Kling 3.0 Omni combines character elements, style references, and multi-shot storyboarding in a single call with native 4K 60fps output.</div>
<p>Kling has the most complete reference image system of any video model. It separates reference inputs into three distinct categories, each serving a different purpose:</p>
<p><strong>Reference Images</strong> (<code>image_urls</code>): Up to 4 images for style and appearance guidance. You tag them in your prompt as <code>@Image1</code>, <code>@Image2</code>, etc. These influence the overall look, scene style, and environment without being the first frame.</p>
<p><strong>Elements</strong> (<code>elements</code>): Dedicated character/object inputs. Each element takes a <code>frontal_image_url</code> (clear front-facing photo) plus optional <code>reference_image_urls</code> (additional angles). You reference them as <code>@Element1</code>, <code>@Element2</code> in your prompt. The model extracts the character's identity and places them in any scene you describe. This is the key feature for adventure-movie-style content: upload a character photo, then describe them walking through a forest, fighting a dragon, whatever you want.</p>
<p><strong>Start/End Frames</strong> (<code>start_image_url</code>, <code>end_image_url</code>): Pin specific images as the first or last frame. These are literal frames, not style guides.</p>
<p>The total across all three categories is up to 7 reference inputs (drops to 4 when also using a reference video). A single prompt like <code>&quot;@Element1 and @Element2 are having dinner at this table on @Image1&quot;</code> can combine characters with scene references.</p>
<p>For long-form content, Kling offers two paths. Multi-shot mode generates up to 6 scenes in a single call, each with its own prompt and duration (3-15s each). Character elements persist across all shots automatically. The extend API continues from where a completed video left off, reaching roughly 3 minutes through chained extensions. The V2V editing mode takes existing video (3-10 seconds) and transforms it using element references and a text prompt, preserving camera motion and character staging from the source while restyling characters and environments based on your references. This makes Kling particularly useful for enhancing pre-existing footage, including low-fidelity 3D renders.</p>
<p>Kling 3.0 Omni unifies text-to-video, image-to-video, reference-to-video, and video editing in a single model with native audio generation and lip-sync.</p>
<p><strong>Availability:</strong> Commercial API through Kuaishou, fal.ai ($0.084-0.112/sec), and Replicate. Web interface at klingai.com.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>3-15 seconds</td>
</tr>
<tr>
<td>Extended length</td>
<td>~3 minutes (via chained extensions)</td>
</tr>
<tr>
<td>Resolution</td>
<td>720p (standard), 1080p (pro)</td>
</tr>
<tr>
<td>Reference images</td>
<td>Up to 4 (<code>@Image</code> style refs)</td>
</tr>
<tr>
<td>Elements</td>
<td>Up to 4 (<code>@Element</code> character refs with frontal + angles)</td>
</tr>
<tr>
<td>Total references</td>
<td>Up to 7 combined (4 with video ref)</td>
</tr>
<tr>
<td>Multi-shot</td>
<td>Yes (up to 6 shots in storyboard)</td>
</tr>
<tr>
<td>Audio</td>
<td>Native synchronized audio + lip-sync</td>
</tr>
<tr>
<td>Video editing</td>
<td>Yes (text-guided editing of existing video)</td>
</tr>
<tr>
<td>API</td>
<td>Kuaishou, fal.ai, Replicate</td>
</tr>
</tbody>
</table>
<h3 id="grok-imagine-xai" tabindex="-1">Grok Imagine (xAI) <a class="header-anchor" href="#grok-imagine-xai" aria-label="Permalink to &quot;Grok Imagine (xAI)&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/DMXA0qGf-1s" title="Grok Imagine: Video Extension and Reference Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Grok Imagine separates reference mode from first-frame mode, letting you tag up to 7 images as character or object references in your prompt.</div>
<p>xAI launched Grok Imagine's Reference-to-Video mode in early 2026 with support for 1-7 reference images. The documentation explicitly distinguishes this from image-to-video: &quot;Unlike image-to-video where the source image becomes the starting frame, reference images influence what appears in the video without locking in the first frame.&quot;</p>
<p>You tag images in your prompt as <code>&lt;IMAGE_1&gt;</code>, <code>&lt;IMAGE_2&gt;</code>, etc. A prompt like <code>&quot;the model from &lt;IMAGE_1&gt; walks onto the runway wearing the shirt from &lt;IMAGE_2&gt;&quot;</code> combines a person reference with a clothing reference. The model handles virtual try-on, product placement, and character-consistent storytelling across scenes.</p>
<p>One constraint: you can't combine reference images with image-to-video in the same request. It's either first-frame mode or reference mode, not both.</p>
<p>Grok Imagine also has a video extension endpoint that adds new footage to the end of an existing video. The <code>duration</code> parameter controls only the new portion. You can chain extensions to build longer content.</p>
<p>In June 2026, xAI released Grok Imagine 1.5 as an API preview (June 3, 2026). It's an image-to-video model that animates a single still into cinematic motion with camera moves, atmosphere, physics, and natively generated synchronized audio in the same inference pass. It supports multi-shot sequencing to chain consistent scenes and currently ranks among the top image-to-video models on the Artificial Analysis arena. Preview pricing is $0.08/sec at 480p and $0.14/sec at 720p, plus $0.01 per input image.</p>
<p><strong>Availability:</strong> xAI API (launched January 2026), fal.ai, and Replicate. Python SDK, JavaScript/AI SDK, and REST API. $0.05/sec at 720p with audio. Also available to X Premium subscribers.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>1-15 seconds</td>
</tr>
<tr>
<td>Extended length</td>
<td>Chain-able via extension API</td>
</tr>
<tr>
<td>Resolution</td>
<td>480p, 720p</td>
</tr>
<tr>
<td>Reference images</td>
<td>1-7 (true reference, not first-frame)</td>
</tr>
<tr>
<td>Prompt tags</td>
<td><code>&lt;IMAGE_1&gt;</code>, <code>&lt;IMAGE_2&gt;</code>, etc.</td>
</tr>
<tr>
<td>Audio</td>
<td>Yes (720p)</td>
</tr>
<tr>
<td>Video editing</td>
<td>Yes (text-guided)</td>
</tr>
<tr>
<td>API</td>
<td>xAI API, fal.ai, Replicate</td>
</tr>
<tr>
<td>API cost</td>
<td>$0.05/second (720p with audio)</td>
</tr>
</tbody>
</table>
<h3 id="seedance-2-0-bytedance" tabindex="-1">Seedance 2.0 (ByteDance) <a class="header-anchor" href="#seedance-2-0-bytedance" aria-label="Permalink to &quot;Seedance 2.0 (ByteDance)&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/W_lxyDFDZt4" title="Seedance 2.0: The New Best AI Video Generator" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Seedance 2.0 accepts up to 12 multimodal inputs simultaneously and generates video with native audio sync and phoneme-level lip-sync in 8+ languages.</div>
<p>ByteDance's Seedance 2.0 accepts the most reference inputs of any model: up to 12 files simultaneously, including up to 9 images, 3 videos, and 3 audio files. The model supports native audio-video generation with phoneme-level lip-sync in 8+ languages.</p>
<p>Individual images can be up to 30MB each. Reference videos must be 2-15 seconds. The model uses the references for character appearance, scene styling, and motion guidance.</p>
<p><strong>Availability:</strong> ByteDance official API (via Volcengine, launched February 2026) and third-party API providers. Output at 480p-720p via API, up to 2K cinema resolution through the platform.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>4-15 seconds</td>
</tr>
<tr>
<td>Resolution</td>
<td>Up to 2K (cinema)</td>
</tr>
<tr>
<td>Reference images</td>
<td>Up to 9 images + 3 videos + 3 audio (12 total)</td>
</tr>
<tr>
<td>Audio</td>
<td>Native with lip-sync (8+ languages)</td>
</tr>
<tr>
<td>API</td>
<td>ByteDance/Volcengine, third-party providers</td>
</tr>
</tbody>
</table>
<h3 id="runway-gen-4-5" tabindex="-1">Runway Gen-4.5 <a class="header-anchor" href="#runway-gen-4-5" aria-label="Permalink to &quot;Runway Gen-4.5&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/AwKSrJFvdps" title="Introducing Gen-4.5 Image to Video | Runway" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Runway Gen-4.5 leads the Artificial Analysis leaderboard at 1,247 ELO, with 3D geometric understanding from neural radiance fields and Gaussian splatting.</div>
<p>Runway Gen-4.5 launched at the top of the Artificial Analysis Text-to-Video leaderboard with 1,247 ELO, ahead of Veo 3 and Sora 2 Pro at the time. By mid-2026 the arena had reshuffled (Seedance 2.0 now leads), but Gen-4.5 remains a top-tier model for cinematic quality and controllable action. The model generates 2-10 second clips for text-to-video and supports character-consistent long-form video up to one minute through multi-shot sequencing.</p>
<p>Image-to-video was added in January 2026 and supports reference images for all aspect ratios. The model integrates neural radiance fields and Gaussian splatting within the diffusion architecture, giving it 3D geometric understanding rather than pixel-level prediction alone. This means better object permanence and physically plausible motion.</p>
<p><strong>Availability:</strong> Commercial API and web interface. SDKs for Node and Python. Also available on Replicate.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>2-10 seconds</td>
</tr>
<tr>
<td>Long-form mode</td>
<td>Up to ~1 minute</td>
</tr>
<tr>
<td>Resolution</td>
<td>Up to 1080p</td>
</tr>
<tr>
<td>Reference images</td>
<td>0-1 per generation</td>
</tr>
<tr>
<td>Audio</td>
<td>Native audio generation</td>
</tr>
<tr>
<td>Multi-shot</td>
<td>Yes</td>
</tr>
<tr>
<td>API</td>
<td>Yes (Runway, Replicate)</td>
</tr>
</tbody>
</table>
<h3 id="google-veo-3-1" tabindex="-1">Google Veo 3.1 <a class="header-anchor" href="#google-veo-3-1" aria-label="Permalink to &quot;Google Veo 3.1&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/I06Ef8alr2Y" title="Veo 3.1: Designed to Empower Creatives" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Veo 3.1's "Ingredients to Video" mode accepts up to 3 reference images for characters, backgrounds, and textures with native audio and 4K upscaling.</div>
<p>Google's Veo 3.1 generates 4, 6, or 8 second clips natively. The &quot;Extend Video&quot; feature (currently in preview) chains clips to reach approximately 1-2.5 minutes, though coherence can drift on longer sequences.</p>
<p>The &quot;Ingredients to Video&quot; feature accepts up to 3 reference images as input. You can provide characters to animate, backgrounds, and material textures. When you use reference images, the model sticks closer to your visual references and makes fewer random alterations. One limitation: reference image mode only works with the 8-second duration option.</p>
<p>As of January 2026, Veo 3.1 added vertical video (9:16) for reference-based generation and 4K upscaling on Vertex AI.</p>
<p><strong>Availability:</strong> Google Vertex AI API, Gemini API, and Google Flow. Requires Google Cloud account.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>4, 6, or 8 seconds</td>
</tr>
<tr>
<td>Extended length</td>
<td>~1-2.5 minutes</td>
</tr>
<tr>
<td>Resolution</td>
<td>Up to 4K (with upscaling)</td>
</tr>
<tr>
<td>Reference images</td>
<td>Up to 3 (&quot;Ingredients to Video&quot;)</td>
</tr>
<tr>
<td>Audio</td>
<td>Synchronized dialogue and music</td>
</tr>
<tr>
<td>API</td>
<td>Vertex AI, Gemini API</td>
</tr>
</tbody>
</table>
<h3 id="openai-sora-2-sora-2-pro" tabindex="-1">OpenAI Sora 2 / Sora 2 Pro <a class="header-anchor" href="#openai-sora-2-sora-2-pro" aria-label="Permalink to &quot;OpenAI Sora 2 / Sora 2 Pro&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/gzneGhpXwjU" title="Introducing Sora 2" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Sora 2 Pro creates persistent character IDs from video clips, reusable across unlimited generations with no identity drift over time.</div>
<p>Sora 2 Pro generates clips up to 20 seconds. The Characters API uses a different approach from Kling or Grok: instead of uploading static images, you create a <code>character_id</code> by pointing the API at a video clip (with a 1-3 second timestamp range). Sora analyzes the video frames to extract facial structure, body proportions, clothing style, and other identifying features. That <code>character_id</code> persists indefinitely and can be reused across unlimited future generations.</p>
<p>You can reference up to 2 uploaded characters per generation. As of March 2026, character references work for objects and animals too, not just people. Video extension uses the full initial clip as context for continuation.</p>
<p>The character system requires video input (not static images) to create characters. If you only have photos, you'd need to generate a short video first, then extract the character from that.</p>
<p><strong>Availability:</strong> OpenAI API with Batch API support for production workflows.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>Up to 20 seconds</td>
</tr>
<tr>
<td>Resolution</td>
<td>Up to 1920x1080</td>
</tr>
<tr>
<td>Character references</td>
<td>Up to 2 per generation (persistent <code>character_id</code>)</td>
</tr>
<tr>
<td>Character input</td>
<td>Video clip (1-3s timestamp range), not static images</td>
</tr>
<tr>
<td>Audio</td>
<td>Synchronized</td>
</tr>
<tr>
<td>Extension</td>
<td>Yes (full clip as context)</td>
</tr>
<tr>
<td>API</td>
<td>OpenAI API + Batch API</td>
</tr>
</tbody>
</table>
<h3 id="minimax-hailuo-02" tabindex="-1">MiniMax Hailuo 02 <a class="header-anchor" href="#minimax-hailuo-02" aria-label="Permalink to &quot;MiniMax Hailuo 02&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/_1a4qYfm5iI" title="Hailuo AI: Director Mode and Subject-to-Video Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Hailuo 02 generates native 1080p video with best-in-class physics simulation, handling extreme motion like gymnastics without breaking apart.</div>
<p>Hailuo 02 ranks #2 globally on the Artificial Analysis benchmark, beating Veo 3. It generates 10-second clips at native 1080p with some of the best physics simulation in the field. The model handles extreme motion like gymnastics and acrobatics without breaking apart.</p>
<p>It supports image-to-video generation with strong character consistency through facial recognition and body tracking. The Noise-aware Compute Redistribution architecture dynamically allocates compute based on scene complexity.</p>
<p>MiniMax has since moved past Hailuo 02 with the Hailuo 2.3 family (Standard, Pro, Fast, Fast Pro), which improves physical action, stylization, and character micro-expressions. It outputs 1080p at 6 seconds or 768p at 10 seconds and is available through the MiniMax platform and fal.ai.</p>
<p><strong>Availability:</strong> Commercial API. Available through MiniMax platform, fal.ai, and Replicate. $0.28 per video.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>Up to 10 seconds</td>
</tr>
<tr>
<td>Resolution</td>
<td>1080p native</td>
</tr>
<tr>
<td>Reference images</td>
<td>Yes (I2V mode)</td>
</tr>
<tr>
<td>Audio</td>
<td>Not native</td>
</tr>
<tr>
<td>Physics</td>
<td>Best-in-class simulation</td>
</tr>
<tr>
<td>API</td>
<td>MiniMax, fal.ai, Replicate</td>
</tr>
</tbody>
</table>
<h3 id="luma-ray2" tabindex="-1">Luma Ray2 <a class="header-anchor" href="#luma-ray2" aria-label="Permalink to &quot;Luma Ray2&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/cnnGMWx-9WU" title="Luma AI - Ray2 Image to Video" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Ray2 animates reference images into 5-10 second clips with photorealistic quality, trained on 10x the compute of its predecessor.</div>
<p>Ray2 generates 5-10 second clips at up to 1080p with 4K upscaling available. The Extend feature continues videos up to 30 seconds total. Image-to-video accepts reference images as start or end keyframes.</p>
<p>The model is trained on a multi-modal architecture with 10x the compute of Ray1. It handles photorealistic content well but the 30-second extension cap limits long-form use.</p>
<p><strong>Availability:</strong> Luma API and web interface.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>5-10 seconds</td>
</tr>
<tr>
<td>Extended length</td>
<td>Up to 30 seconds</td>
</tr>
<tr>
<td>Resolution</td>
<td>Up to 4K (with upscaling)</td>
</tr>
<tr>
<td>Reference images</td>
<td>Yes (start/end keyframes)</td>
</tr>
<tr>
<td>API</td>
<td>Luma API</td>
</tr>
</tbody>
</table>
<h3 id="pika-2-5" tabindex="-1">Pika 2.5 <a class="header-anchor" href="#pika-2-5" aria-label="Permalink to &quot;Pika 2.5&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/JxgUkF_2alk" title="Pika 2.2: PikaFrames Keyframe Video Generation" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Pikaframes generates smooth transitions between 2-5 keyframe images, producing up to 25 seconds of coherent video from reference stills.</div>
<p>Pika takes a keyframe-based approach with Pikaframes. Upload 2-5 keyframes (reference images at key moments) and the model generates smooth transitions between them. Total duration reaches 20-25 seconds.</p>
<p>Pikascenes accepts up to 10 reference images and combines them into a single video. The model uses image recognition to figure out each reference's role (character, background, prop) automatically.</p>
<p><strong>Availability:</strong> Pika web platform and API. Subscription plans from free to Pro.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Native clip length</td>
<td>5-10 seconds</td>
</tr>
<tr>
<td>Pikaframes length</td>
<td>20-25 seconds</td>
</tr>
<tr>
<td>Resolution</td>
<td>Up to 1080p</td>
</tr>
<tr>
<td>Reference images</td>
<td>Up to 10 (Pikascenes), 2-5 keyframes (Pikaframes)</td>
</tr>
<tr>
<td>API</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<h2 id="tier-3-open-source-models-for-self-hosted-workflows" tabindex="-1">Tier 3: Open-Source Models for Self-Hosted Workflows <a class="header-anchor" href="#tier-3-open-source-models-for-self-hosted-workflows" aria-label="Permalink to &quot;Tier 3: Open-Source Models for Self-Hosted Workflows&quot;"></a></h2>
<p>These models generate shorter clips but they're fully open. You can run them on your own hardware, fine-tune them, and build custom extension pipelines without API dependencies.</p>
<h3 id="wan-2-1-alibaba" tabindex="-1">Wan 2.1 (Alibaba) <a class="header-anchor" href="#wan-2-1-alibaba" aria-label="Permalink to &quot;Wan 2.1 (Alibaba)&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/2CObXv_CLyY" title="Introducing Wan2.1: AI Video Generation" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">Wan 2.1 provides the foundation several other models build on, with I2V, First-Last-Frame, and video editing modes across 1.3B to 14B parameter variants.</div>
<p>Wan 2.1 is the foundation several other models build on (including Helios). The Wan-VAE architecture encodes and decodes 1080p video of any length while preserving temporal information. The model comes in I2V variants at 480p and 720p, plus a First-Last-Frame-to-Video model that generates video between two reference images.</p>
<p>Wan-Edit allows style and content transfer using reference images while maintaining specific structures or character poses.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Parameters</td>
<td>1.3B, 5B, 14B</td>
</tr>
<tr>
<td>I2V modes</td>
<td>I2V-480P, I2V-720P, FLF2V-720P</td>
</tr>
<tr>
<td>License</td>
<td>Apache 2.0</td>
</tr>
<tr>
<td>Hardware</td>
<td>8GB+ VRAM (smaller variants)</td>
</tr>
<tr>
<td>Platforms</td>
<td>Diffusers, ComfyUI</td>
</tr>
</tbody>
</table>
<h3 id="hunyuanvideo-tencent" tabindex="-1">HunyuanVideo (Tencent) <a class="header-anchor" href="#hunyuanvideo-tencent" aria-label="Permalink to &quot;HunyuanVideo (Tencent)&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/tqteD9U2R_4" title="HunyuanVideo: Tencent's Open-Source Video Generator" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">HunyuanVideo's 13B parameter model was the open-source leader through most of 2025, with variants for I2V, avatars, and customized generation.</div>
<p>Tencent's 13B parameter model was the open-source video generation leader through most of 2025. HunyuanVideo-I2V uses a token replace technique with a pre-trained MLLM to incorporate reference image information. HunyuanVideo-1.5, released November 2025, improved efficiency. HunyuanCustom enables multimodal-driven customized video generation.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Parameters</td>
<td>13B</td>
</tr>
<tr>
<td>I2V</td>
<td>Yes (token replace technique)</td>
</tr>
<tr>
<td>License</td>
<td>Open source</td>
</tr>
<tr>
<td>Hardware</td>
<td>60GB+ VRAM (720p)</td>
</tr>
<tr>
<td>Variants</td>
<td>Base, I2V, 1.5, Avatar, Custom</td>
</tr>
</tbody>
</table>
<h3 id="cogvideox-tsinghua-zhipu-ai" tabindex="-1">CogVideoX (Tsinghua/Zhipu AI) <a class="header-anchor" href="#cogvideox-tsinghua-zhipu-ai" aria-label="Permalink to &quot;CogVideoX (Tsinghua/Zhipu AI)&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/eg2S52BYjjc" title="CogVideoX: Text to Video Generation with 2B and 5B Models" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="video-caption">CogVideoX runs on a 12GB GPU, generating 6-10 second clips at 720x480 with text-to-video, image-to-video, and video-to-video modes.</div>
<p>CogVideoX uses a 3D causal VAE that reduces sequence length and prevents flickering. The adaptive LayerNorm transformer improves text-video alignment. Available in 2B (Apache 2.0) and 5B (research license) variants with native Diffusers integration.</p>
<p>Clips are 6-10 seconds at 720x480. Short, but the quality-to-compute ratio is good and it runs on a 12GB GPU.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Parameters</td>
<td>2B, 5B</td>
</tr>
<tr>
<td>I2V</td>
<td>Yes (CogVideoXImageToVideoPipeline)</td>
</tr>
<tr>
<td>Resolution</td>
<td>720x480 at 8fps</td>
</tr>
<tr>
<td>License</td>
<td>Apache 2.0 (2B), Research (5B)</td>
</tr>
<tr>
<td>Hardware</td>
<td>12GB VRAM</td>
</tr>
</tbody>
</table>
<h2 id="first-frame-vs-true-reference-the-key-distinction" tabindex="-1">First-Frame vs. True Reference: The Key Distinction <a class="header-anchor" href="#first-frame-vs-true-reference-the-key-distinction" aria-label="Permalink to &quot;First-Frame vs. True Reference: The Key Distinction&quot;"></a></h2>
<p>Not all &quot;reference image&quot; support is the same. Understanding the difference is critical for choosing the right model.</p>
<p><strong>First-frame models</strong> (LongCat, Helios, Hailuo, Luma Ray2, HunyuanVideo) treat your image as the literal opening frame. The model animates forward from that exact visual. You can't upload a character headshot and describe them in a different scene. The image is the scene.</p>
<p><strong>True reference models</strong> (Kling, Grok Imagine, Seedance, SkyReels V3) extract identity from your image and place that character/object into any scene you describe. Upload a photo of a person, then prompt &quot;that person walks through a forest at sunset.&quot; The character appears in a completely new environment while maintaining their identity. This is what you need for multi-scene narrative content like an adventure movie.</p>
<p><strong>Character ID models</strong> (Sora 2 Pro) extract identity from video clips rather than static images. You create a persistent character ID once and reuse it across unlimited future generations.</p>
<p><strong>Style/ingredient models</strong> (Veo 3.1) use reference images to influence visual style, textures, and overall look rather than extracting specific character identities. Good for maintaining visual consistency across a project, less precise for individual character control.</p>
<h2 id="the-real-workflow-for-10-minute-videos" tabindex="-1">The Real Workflow for 10-Minute Videos <a class="header-anchor" href="#the-real-workflow-for-10-minute-videos" aria-label="Permalink to &quot;The Real Workflow for 10-Minute Videos&quot;"></a></h2>
<p>Here's the honest take on where things stand in mid-2026. No single model reliably generates 10 minutes of consistent, high-quality video in one shot. LongCat Video gets closest with claims of 15 minutes, but quality and coherence vary significantly at those lengths. Helios and SkyReels V2 generate &quot;minute-scale&quot; and &quot;infinite-length&quot; video respectively, but the outputs need careful prompting and often multiple attempts.</p>
<p>The workflow that actually works for most creators building 5-15 minute videos combines multiple approaches:</p>
<p><strong>For talking head / avatar content:</strong> LongCat Video's 2026 audio-driven mode or SkyReels V3's avatar generation can produce 5+ minutes of a consistent talking character. This is the closest thing to &quot;press a button, get long video.&quot;</p>
<p><strong>For narrative content with multiple scenes (adventure movie style):</strong> Use Kling 3.0, Grok Imagine, or Seedance 2.0 with true character reference images. Generate individual shots of 10-15 seconds each. Use the same @Element or <code>&lt;IMAGE&gt;</code> references across every generation to maintain character identity. Chain shots together using multi-shot mode (Kling supports 6 shots per call) or the extend API. Kling is the most battle-tested for this workflow. Grok Imagine's explicit separation between &quot;reference mode&quot; and &quot;first-frame mode&quot; makes it a strong alternative. Seedance 2.0 accepts the most reference inputs (12 files) but is newer and less proven.</p>
<p><strong>For character consistency across many clips:</strong> Sora 2 Pro's persistent <code>character_id</code> system is the cleanest approach for very long projects. Extract the character once from a short video, then generate dozens of clips referencing that ID. The character identity doesn't degrade over time because it's stored as a persistent embedding, not re-interpreted from an image each time.</p>
<p><strong>For style-transferred content:</strong> Lucy Restyle on fal.ai processes existing video up to 30 minutes, applying AI style transformations while preserving motion. If you have source footage, this sidesteps the generation length problem entirely. $0.01 per second of source video.</p>
<p><strong>For open-source pipelines:</strong> Build on Wan 2.1 or Helios with a video continuation loop. Generate a clip, use the last frame as the start frame for the next clip, repeat. ComfyUI workflows automate this. Consistency degrades over many iterations but it's free and controllable.</p>
<p>The core challenge remains: even with true reference image support, character drift compounds across dozens of clips. Facial features, hair, clothing, and skin tone gradually shift. The workarounds (high-quality reference photos, consistent prompting, shot batching) are necessary. But models like Kling and Grok Imagine that separate character identity from scene composition make this dramatically easier than the first-frame-only models.</p>
<h2 id="the-3d-scaffold-approach-render-low-transform-high" tabindex="-1">The 3D Scaffold Approach: Render Low, Transform High <a class="header-anchor" href="#the-3d-scaffold-approach-render-low-transform-high" aria-label="Permalink to &quot;The 3D Scaffold Approach: Render Low, Transform High&quot;"></a></h2>
<p>There's a workflow gaining traction that sidesteps most long-form generation problems entirely. Instead of asking an AI model to generate 10 minutes of video from scratch, you render a low-fidelity 3D cutscene with correct camera work, character blocking, and timing, then run it through a video-to-video model with reference images and an enhancement prompt. The 3D engine handles structure. The AI handles aesthetics.</p>
<p>This works because V2V transformation is a narrower problem than full generation. The model doesn't need to invent camera motion, character placement, or scene composition. It just needs to make existing footage look photorealistic while following your visual references. That's far more tractable, and it scales to any length your 3D engine can render.</p>
<h3 id="why-this-works" tabindex="-1">Why This Works <a class="header-anchor" href="#why-this-works" aria-label="Permalink to &quot;Why This Works&quot;"></a></h3>
<p>Your 3D engine gives you everything AI video models still struggle with: precise camera control, exact character placement across the frame, correct physics interactions, and consistent timing across minutes of footage. Dolly zooms, tracking shots, characters entering and exiting frame on cue, all trivial in a 3D engine, all unreliable in text-prompted generation. The V2V model's only job is transforming materials, lighting, and textures into photorealistic output while preserving the geometry and motion you already defined.</p>
<p>Character consistency gets easier too. Instead of fighting identity drift across 50 separate AI generations, you're showing the model the same 3D character in every frame. The reference images tell the model what that character should look like in the final output. That's a simpler problem than generating a consistent character from scratch each time.</p>
<p>And length is no longer a constraint. Lucy Restyle handles 30 minutes in a single call. Wan 2.1 in ComfyUI can process any length in chunks. You're not fighting the &quot;how do I generate 10 minutes&quot; problem at all because the footage already exists.</p>
<h3 id="realmaster-meta-tel-aviv-university" tabindex="-1">RealMaster (Meta / Tel Aviv University) <a class="header-anchor" href="#realmaster-meta-tel-aviv-university" aria-label="Permalink to &quot;RealMaster (Meta / Tel Aviv University)&quot;"></a></h3>
<p>RealMaster is a research system built specifically for this workflow. Published March 2026 by Meta Reality Labs and Tel Aviv University, it transforms rendered 3D video into photorealistic video while maintaining full geometric alignment with the source.</p>
<p>The method extracts edge maps from the 3D render to preserve structure and motion, then applies a video diffusion model (built on the VACE/Wan architecture) to transform everything else into photorealistic output. A lightweight IC-LoRA adapter distills the pipeline into a single inference pass that doesn't need anchor frames and handles objects appearing mid-sequence.</p>
<p>Tested on GTA-V and CARLA simulator sequences, RealMaster significantly outperforms general-purpose video editing baselines. It can also layer weather effects through the text prompt (&quot;Make it rain&quot;, &quot;Make it snow&quot;) on top of the realism transformation. The model generalizes across simulators without retraining. Weights trained on GTA-V data work on CARLA output with no additional tuning.</p>
<p><strong>Availability:</strong> Research only. No public weights or API yet.</p>
<table tabindex="0">
<thead>
<tr>
<th>Spec</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Input</td>
<td>Rendered 3D video (any engine)</td>
</tr>
<tr>
<td>Output</td>
<td>Photorealistic video preserving geometry and motion</td>
</tr>
<tr>
<td>Architecture</td>
<td>IC-LoRA on VACE/Wan video diffusion backbone</td>
</tr>
<tr>
<td>Conditioning</td>
<td>Edge maps from source render</td>
</tr>
<tr>
<td>Tested on</td>
<td>GTA-V, CARLA simulator</td>
</tr>
<tr>
<td>License</td>
<td>Not released (research paper only)</td>
</tr>
</tbody>
</table>
<h3 id="production-v2v-tools-available-today" tabindex="-1">Production V2V Tools Available Today <a class="header-anchor" href="#production-v2v-tools-available-today" aria-label="Permalink to &quot;Production V2V Tools Available Today&quot;"></a></h3>
<p><strong>Kling 3.0 V2V with Element References</strong> is the most complete production option. The Edit Video and Reference V2V endpoints on fal.ai accept 3-10 second source video clips alongside element references (<code>@Element1</code>, <code>@Element2</code> with frontal and multi-angle photos) and an enhancement prompt. The model analyzes motion trajectories and camera patterns in the source, then regenerates the footage with your specified character appearances and visual style while preserving the original staging and camera work. Up to 7 reference inputs. Output at 1080p. Process your cutscene in 10-15 second chunks with the same element references across all chunks to maintain character consistency.</p>
<p><strong>Lucy Restyle 2</strong> handles up to 30 minutes of source video in a single API call at $0.01 per second of input. It accepts a text prompt and an optional reference image for style guidance. No per-character element references like Kling, but for overall cinematic style transfer of a full-length 3D render it's the simplest and cheapest path. Feed it your complete render and a prompt describing the target look. Output at 720p with temporal consistency across thousands of frames.</p>
<p><strong>Wan 2.1 VACE in ComfyUI</strong> is the open-source route. The 14B VACE model does reference-driven V2V: input a source video plus a style reference image, output a restyled version that preserves structure and motion. Edge map conditioning improves structural fidelity. You can build a processing loop that handles any length in chunks with consistent style references. Free, runs locally on your own hardware.</p>
<p><strong>Grok Imagine V2V</strong> accepts source video plus 1-7 reference images in reference mode. $0.05 per second at 720p. The explicit separation between reference mode and first-frame mode means your references guide character appearance without overriding the source video's structure.</p>
<h3 id="what-your-3d-render-needs" tabindex="-1">What Your 3D Render Needs <a class="header-anchor" href="#what-your-3d-render-needs" aria-label="Permalink to &quot;What Your 3D Render Needs&quot;"></a></h3>
<p>The render quality floor matters. A bare wireframe won't give the V2V model enough to work with. But you don't need production-quality materials or lighting either.</p>
<p><strong>Correct proportions and geometry.</strong> Character models need roughly correct body proportions and facial structure for the reference images to map properly. Basic humanoid geometry with correct proportions is enough. A stick figure won't produce a recognizable character.</p>
<p><strong>Basic lighting direction.</strong> A single directional light establishing the scene's overall illumination helps the model understand the intended mood. The AI will enhance and add detail, but it needs to know whether the scene is bright daylight or dark interior.</p>
<p><strong>Smooth camera motion.</strong> Stable, deliberate camera moves translate well. Erratic or extremely fast motion can confuse V2V models. Keep your virtual camera behaving like a real camera would.</p>
<p><strong>Flat shading over wireframe.</strong> Simple flat-shaded or low-poly geometry gives better results than wireframes or untextured models. Even basic solid colors on surfaces help the model understand material boundaries.</p>
<h3 id="cost-and-scale" tabindex="-1">Cost and Scale <a class="header-anchor" href="#cost-and-scale" aria-label="Permalink to &quot;Cost and Scale&quot;"></a></h3>
<p>Processing a 10-minute cutscene through different tools:</p>
<table tabindex="0">
<thead>
<tr>
<th>Tool</th>
<th>Max Input Length</th>
<th>Cost for 10 min</th>
<th>Resolution</th>
<th>Reference Images</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kling O3 V2V</td>
<td>10s clips</td>
<td>~$50-67</td>
<td>1080p</td>
<td>Up to 7 (elements + style)</td>
</tr>
<tr>
<td>Lucy Restyle 2</td>
<td>30 minutes</td>
<td>$6</td>
<td>720p</td>
<td>1 (style only)</td>
</tr>
<tr>
<td>Grok Imagine V2V</td>
<td>10s clips</td>
<td>~$30</td>
<td>720p</td>
<td>1-7</td>
</tr>
<tr>
<td>Wan 2.1 VACE</td>
<td>Any (chunked)</td>
<td>Free (local GPU)</td>
<td>720p</td>
<td>1 per chunk</td>
</tr>
</tbody>
</table>
<p>Lucy Restyle is the cheapest for full-length processing. Kling is the most precise for character-specific enhancement with element references. Wan 2.1 is free if you have the hardware (needs about 60GB VRAM for the 14B model at 720p, or 8GB for the 1.3B variant at lower quality).</p>
<h2 id="comparison-table" tabindex="-1">Comparison Table <a class="header-anchor" href="#comparison-table" aria-label="Permalink to &quot;Comparison Table&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Max Native Duration</th>
<th>Extended Duration</th>
<th>Reference Type</th>
<th>Max Refs</th>
<th>Resolution</th>
<th>API Available</th>
<th>Open Source</th>
</tr>
</thead>
<tbody>
<tr>
<td>LongCat Video</td>
<td>~15 min</td>
<td>N/A</td>
<td>First-frame only</td>
<td>1</td>
<td>720p/30fps</td>
<td>Yes (fal.ai)</td>
<td>Yes (MIT)</td>
</tr>
<tr>
<td>Seaweed APT2</td>
<td>~5 min</td>
<td>N/A</td>
<td>I2V + pose</td>
<td>1</td>
<td>720p</td>
<td>No</td>
<td>No</td>
</tr>
<tr>
<td>Helios</td>
<td>Minute-scale</td>
<td>N/A</td>
<td>First-frame (I2V)</td>
<td>1</td>
<td>720p</td>
<td>HF Spaces</td>
<td>Yes (Apache 2.0)</td>
</tr>
<tr>
<td>SkyReels V3</td>
<td>Unlimited</td>
<td>N/A</td>
<td>True reference</td>
<td>1-4</td>
<td>720p</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td><strong>Kling 3.0</strong></td>
<td>15s</td>
<td>~3 min</td>
<td><strong>Elements + style refs</strong></td>
<td><strong>7</strong></td>
<td>1080p</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td><strong>Grok Imagine</strong></td>
<td>15s</td>
<td>Chain-able</td>
<td><strong>True reference</strong></td>
<td><strong>7</strong></td>
<td>720p</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td><strong>Seedance 2.0</strong></td>
<td>15s</td>
<td>N/A</td>
<td><strong>Multi-modal refs</strong></td>
<td><strong>12</strong></td>
<td>2K</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Runway Gen-4.5</td>
<td>10s</td>
<td>~1 min</td>
<td>I2V (0-1)</td>
<td>1</td>
<td>1080p</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Veo 3.1</td>
<td>8s</td>
<td>~2.5 min</td>
<td>Ingredients (style)</td>
<td>3</td>
<td>4K</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Sora 2 Pro</td>
<td>20s</td>
<td>Chain-able</td>
<td>Character ID (video)</td>
<td>2</td>
<td>1080p</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Hailuo 02</td>
<td>10s</td>
<td>N/A</td>
<td>I2V (first-frame)</td>
<td>1</td>
<td>1080p</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Luma Ray2</td>
<td>10s</td>
<td>30s</td>
<td>First-frame</td>
<td>1</td>
<td>4K</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Pika 2.5</td>
<td>10s</td>
<td>25s</td>
<td>Pikascenes</td>
<td>10</td>
<td>1080p</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Wan 2.1</td>
<td>Short clips</td>
<td>Via continuation</td>
<td>I2V / FLF2V</td>
<td>1-2</td>
<td>720p</td>
<td>Via fal.ai</td>
<td>Yes (Apache 2.0)</td>
</tr>
<tr>
<td>HunyuanVideo</td>
<td>Short clips</td>
<td>Via continuation</td>
<td>I2V (first-frame)</td>
<td>1</td>
<td>720p</td>
<td>Via fal.ai</td>
<td>Yes</td>
</tr>
<tr>
<td>CogVideoX</td>
<td>6-10s</td>
<td>Via continuation</td>
<td>I2V (first-frame)</td>
<td>1</td>
<td>720x480</td>
<td>Via fal.ai</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<h2 id="what-s-coming" tabindex="-1">What's Coming <a class="header-anchor" href="#what-s-coming" aria-label="Permalink to &quot;What's Coming&quot;"></a></h2>
<p>The trajectory through 2026 is clear. LongCat Video proved that minute-scale generation with consistency is possible in an open model. Helios showed it can happen in real-time. Seaweed APT2 demonstrated interactive long-form generation. And the true-reference models (Kling, Grok, Seedance) proved that character identity can persist across arbitrary scenes.</p>
<p>The next step is combining these capabilities: native long-form generation with true character reference support. Right now you pick one or the other. When a model can generate 5 minutes of video while maintaining characters from reference images across dozens of scene changes, the chained-clips workflow becomes obsolete.</p>
<p>As of mid-2026, the Artificial Analysis text-to-video arena (with audio) is led by Seedance 2.0 (~1,215 Elo), followed by HappyHorse-1.0, SkyReels V4, and several Kling 3.0 variants, with Veo 3.1 and Sora 2 close behind. The leaderboard reshuffles frequently, so treat any single ranking as a snapshot rather than a fixed order.</p>
<p>The 3D scaffold approach offers a parallel trajectory. As V2V models improve their structural preservation and photorealism, enhancing low-fidelity 3D renders becomes increasingly viable for full production. RealMaster from Meta already achieves research-quality sim-to-real transformation on game engine output. When this capability hits production APIs with reference image support, anyone with basic 3D skills will be able to produce photorealistic long-form video with complete control over camera, staging, and character placement at any duration.</p>
<p>For now, the practical answer depends on your use case:</p>
<p><strong>Best for multi-character reference:</strong> Kling 3.0 (up to 7 refs with separate element + style system) or Seedance 2.0 (up to 12 multimodal inputs).</p>
<p><strong>Best API for reference-to-video:</strong> Grok Imagine (clean API, explicit reference mode, $0.05/sec) or Kling via fal.ai ($0.084-0.112/sec).</p>
<p><strong>Best for persistent characters across many clips:</strong> Sora 2 Pro (character ID system, no drift over time).</p>
<p><strong>Best open source:</strong> SkyReels V3 (1-4 true reference images, unlimited length) or Helios (real-time, Apache 2.0).</p>
<p><strong>Best for raw duration:</strong> LongCat Video (~15 min, but first-frame only).</p>
<p><strong>Best for 3D render enhancement:</strong> Kling 3.0 V2V (per-character element refs, 1080p) or Lucy Restyle 2 (30 min input, $0.01/sec).</p>
<hr>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="what-is-the-best-ai-video-model-for-long-videos" tabindex="-1">What is the best AI video model for long videos? <a class="header-anchor" href="#what-is-the-best-ai-video-model-for-long-videos" aria-label="Permalink to &quot;What is the best AI video model for long videos?&quot;"></a></h3>
<p>For raw duration, LongCat Video generates around 15 minutes natively, though it's first-frame only. For long video with consistent characters, the practical answer in 2026 is a reference-and-extend workflow: generate clips with a model that has strong reference support (Kling 3.0, Runway Gen-4.5, or open-source SkyReels V3), then chain them. No single model both runs long and holds character identity perfectly, so most production work combines them.</p>
<h3 id="which-ai-video-models-support-reference-images" tabindex="-1">Which AI video models support reference images? <a class="header-anchor" href="#which-ai-video-models-support-reference-images" aria-label="Permalink to &quot;Which AI video models support reference images?&quot;"></a></h3>
<p>Kling 3.0 Omni, Runway Gen-4.5, Seedance 2.0, and Google Veo 3.1 all support reference images among the commercial options. On the open-source side, SkyReels V2/V3 and Wan 2.1 accept reference inputs you can run yourself. Support quality varies a lot, which is why the guide above splits them into tiers.</p>
<h3 id="can-ai-generate-a-consistent-character-across-a-long-video" tabindex="-1">Can AI generate a consistent character across a long video? <a class="header-anchor" href="#can-ai-generate-a-consistent-character-across-a-long-video" aria-label="Permalink to &quot;Can AI generate a consistent character across a long video?&quot;"></a></h3>
<p>Yes, but not in one shot. The reliable approach is to lock a character with one or more reference images, generate short clips, and extend or stitch them while feeding the same references back in. True reference support (the model keeps identity across new generations) matters far more here than first-frame conditioning, which only seeds the opening frame.</p>
<h3 id="what-is-the-difference-between-first-frame-and-true-reference-image-support" tabindex="-1">What is the difference between first-frame and true reference image support? <a class="header-anchor" href="#what-is-the-difference-between-first-frame-and-true-reference-image-support" aria-label="Permalink to &quot;What is the difference between first-frame and true reference image support?&quot;"></a></h3>
<p>First-frame conditioning uses your image as the literal opening frame of the clip, then drifts as the video progresses. True reference support treats the image as an identity anchor the model honors throughout the generation, so a character or style stays consistent across the whole clip and across separate clips. The section above breaks down which models do which.</p>
<hr>
<h2 id="more-reading" tabindex="-1">More Reading <a class="header-anchor" href="#more-reading" aria-label="Permalink to &quot;More Reading&quot;"></a></h2>
<ul>
<li><a href="/guides/frontier-gen-ai-models.html">Frontier Open-Source Gen AI Models</a> — practical guide to open-source generative AI for video, image, 3D, audio, and more</li>
<li><a href="/tools/video.html">Video Generator</a> — our video generation tool powered by Kling 3.0 Pro</li>
<li><a href="/blog/2026-03-04-sketch-to-animated-3d-character.html">How to go from sketch to animated 3D character</a> — using image and video generation for character animation</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Landscape Generation with Dynamic LOD and Streaming for Browser Open Worlds]]></title>
            <link>https://app.cinevva.com/guides/landscape-generation-browser</link>
            <guid>https://app.cinevva.com/guides/landscape-generation-browser</guid>
            <pubDate>Sun, 22 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Beyond heightmaps and noise functions. Physically correct terrain generation, volumetric representations, diffusion-based synthesis, and GPU-driven rendering pipelines for browser-based open worlds.]]></description>
            <content:encoded><![CDATA[<h1 id="landscape-generation-with-dynamic-lod-and-streaming-for-browser-open-worlds" tabindex="-1">Landscape Generation with Dynamic LOD and Streaming for Browser Open Worlds <a class="header-anchor" href="#landscape-generation-with-dynamic-lod-and-streaming-for-browser-open-worlds" aria-label="Permalink to &quot;Landscape Generation with Dynamic LOD and Streaming for Browser Open Worlds&quot;"></a></h1>
<p>Heightmaps with Perlin noise are the default starting point for procedural terrain. Layer some octaves of fBm, apply a color gradient, and you have something that looks terrain-ish. Every tutorial ends here. But real landscapes don't look like stacked noise. They have river valleys carved by water, cliff faces shaped by stress fractures, caves and arches formed by erosion over millions of years. They have correlated textures (grass on flat ground, rock on steep slopes, snow above the treeline) that emerge from the same physical processes that shaped the geometry.</p>
<p>This guide covers what comes after noise. Physically grounded generation methods, volumetric representations that handle caves and overhangs, diffusion-based neural terrain synthesis, and the GPU-driven LOD and streaming pipelines needed to render it all in a browser tab at 60fps.</p>
<p>Everything here targets our specific constraint: a multiplayer open world running in WebGL 2 / WebGPU, streamed over the network, editable by creators.</p>
<h2 id="why-heightmaps-aren-t-enough" tabindex="-1">Why Heightmaps Aren't Enough <a class="header-anchor" href="#why-heightmaps-aren-t-enough" aria-label="Permalink to &quot;Why Heightmaps Aren't Enough&quot;"></a></h2>
<p>A heightmap stores one height value per grid point. It's a 2D function: given (x, z), return y. This representation is compact, GPU-friendly, and fast to render. But it has fundamental limitations that matter for a creator world.</p>
<p><strong>No caves or overhangs.</strong> A heightmap can't represent terrain where one point has two different heights. Caves, arches, cliff overhangs, tunnels, and floating islands are all impossible. Minecraft, No Man's Sky, and Deep Rock Galactic all need volumetric terrain for this reason.</p>
<p><strong>No vertical features.</strong> A vertical cliff face in a heightmap is a near-infinite slope, which creates extreme texture stretching and collision artifacts. Real cliffs have horizontal features (ledges, crevices) that a heightmap can't represent.</p>
<p><strong>Noise looks like noise.</strong> Even with 8 octaves of fBm and domain warping, the terrain has a synthetic quality. It lacks the directional structure of real geology: ridge lines, drainage networks, sediment deposits, tectonic folds. These patterns emerge from physical processes, not from noise functions.</p>
<p><strong>Creator edits are limited.</strong> If creators can only modify heights, they can't dig tunnels, create caves, or build underground spaces. For a world that creators can truly shape, the terrain representation needs to support subtraction as well as addition.</p>
<p>The solution isn't to abandon heightmaps entirely. They remain the best representation for the 90% of terrain that is a simple surface. The solution is a hybrid approach: heightmap base terrain with volumetric overlays where complex geometry is needed, physically correct generation for realistic landforms, and neural synthesis for the diversity that noise can't achieve.</p>
<h2 id="the-quick-answer-what-we-d-actually-build" tabindex="-1">The Quick Answer: What We'd Actually Build <a class="header-anchor" href="#the-quick-answer-what-we-d-actually-build" aria-label="Permalink to &quot;The Quick Answer: What We'd Actually Build&quot;"></a></h2>
<p>Before the deep dive, here's the decision framework. The rest of the article explains each piece.</p>
<h3 id="terrain-representation" tabindex="-1">Terrain representation <a class="header-anchor" href="#terrain-representation" aria-label="Permalink to &quot;Terrain representation&quot;"></a></h3>
<p>Use a <strong>hybrid heightmap + SDF</strong> system. The heightmap covers the entire world (cheap, compact, proven). SDF volumes exist only where caves, overhangs, or creator-carved features require them (maybe 5-10% of chunks). This keeps 90% of the world at heightmap cost while supporting arbitrary geometry where needed.</p>
<h3 id="generation-pipeline" tabindex="-1">Generation pipeline <a class="header-anchor" href="#generation-pipeline" aria-label="Permalink to &quot;Generation pipeline&quot;"></a></h3>
<p>Run server-side as a chained process:</p>
<ol>
<li><strong>Terrain Diffusion</strong> (or MESA for text prompts) generates the base heightmap from a seed. This replaces noise with geologically realistic landforms trained on real elevation data.</li>
<li><strong>Analytical erosion</strong> (stream power law) refines the heightmap with river networks and ridgelines in milliseconds.</li>
<li><strong>TerraFusion or Geodiffussr</strong> generates correlated textures from the heightmap (or use procedural slope/altitude rules for the WebGL 2 fallback).</li>
<li><strong>Ecosystem simulation</strong> produces vegetation density maps.</li>
<li><strong>Arenite-style erosion</strong> generates SDF volumes for cliff faces, arches, and caves where the terrain calls for them.</li>
<li><strong>Chunk, compress, upload to CDN.</strong> Average chunk: 2-8 KB. Complex chunk with volumetric data: 20-100 KB.</li>
</ol>
<p>Creators interact with this pipeline by adjusting parameters (&quot;wetter,&quot; &quot;more mountainous,&quot; &quot;add caves&quot;), sketching intent, or directly sculpting with brushes and SDF tools.</p>
<h3 id="lod-strategy" tabindex="-1">LOD strategy <a class="header-anchor" href="#lod-strategy" aria-label="Permalink to &quot;LOD strategy&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Browser capability</th>
<th>Heightmap LOD</th>
<th>Volumetric LOD</th>
<th>Vegetation LOD</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>WebGPU</strong></td>
<td>GPU-driven quadtree (CDLOD) with compute culling + indirect draw</td>
<td>Multi-resolution SDF with compute marching cubes + Transvoxel</td>
<td>ComputeInstanceCulling with indirect draw</td>
</tr>
<tr>
<td><strong>WebGL 2</strong></td>
<td>Geometry clipmaps with CPU-side ring updates</td>
<td>Pre-generated meshes at 2-3 LOD levels, cached</td>
<td>CPU frustum cull, InstancedMesh</td>
</tr>
</tbody>
</table>
<p>Both paths use geomorphing in the vertex shader for pop-free transitions. Both use billboard impostors for distant vegetation. The WebGPU path is faster (3.5ms terrain budget) but the WebGL 2 path is viable (6.5ms).</p>
<h3 id="streaming" tabindex="-1">Streaming <a class="header-anchor" href="#streaming" aria-label="Permalink to &quot;Streaming&quot;"></a></h3>
<p>Progressive loading: terrain geometry first (&lt;100ms), textures second (&lt;300ms), vegetation third (&lt;1s), volumetric data fourth (&lt;3s). Pre-fetch based on player velocity. Memory budget: 256 MB total terrain.</p>
<h3 id="editing" tabindex="-1">Editing <a class="header-anchor" href="#editing" aria-label="Permalink to &quot;Editing&quot;"></a></h3>
<p>Heightmap brushes for surface sculpting (raise, lower, smooth, erode). SDF primitives for volumetric editing (carve caves, add arches). Both are instant locally, sync to server in 100-300ms, broadcast to other players via delta updates.</p>
<h3 id="what-to-build-in-what-order" tabindex="-1">What to build in what order <a class="header-anchor" href="#what-to-build-in-what-order" aria-label="Permalink to &quot;What to build in what order&quot;"></a></h3>
<p><strong>Month 1-2: Heightmap terrain with geometry clipmaps.</strong> WebGL 2 only. Streaming from CDN. Procedural slope/altitude materials. This gets terrain on screen in every browser. Use noise + analytical erosion for generation (Terrain Diffusion can wait).</p>
<p><strong>Month 3-4: Vegetation and atmosphere.</strong> GPU-instanced grass and trees from density maps. Procedural sky with day/night cycle. Atmospheric fog. Cascaded shadow maps. The world starts to feel like a place.</p>
<p><strong>Month 5-6: Creator editing.</strong> Heightmap brush tools. Delta-based sync for multiplayer terrain edits. Spatial locking for concurrent editing. This is when creators start shaping the world.</p>
<p><strong>Month 7-9: Volumetric terrain and WebGPU path.</strong> SDF overlays for caves and overhangs. Marching cubes in WebGPU compute. Transvoxel for LOD boundaries. SDF sculpting tools. This unlocks the full creation toolkit.</p>
<p><strong>Month 10-12: Neural generation and polish.</strong> Terrain Diffusion or MESA for the base heightmap. TerraFusion/Geodiffussr for textures. Phasor noise for micro-detail. Hex-tiling for anti-repetition. Virtual texturing. Laplacian blending. The terrain reaches production quality.</p>
<p>This ordering means something playable exists after 2 months, something beautiful after 4, something editable after 6, and something state-of-the-art after 12.</p>
<p>The rest of this article is the research behind each decision.</p>
<h2 id="terrain-representations-beyond-heightmaps" tabindex="-1">Terrain Representations Beyond Heightmaps <a class="header-anchor" href="#terrain-representations-beyond-heightmaps" aria-label="Permalink to &quot;Terrain Representations Beyond Heightmaps&quot;"></a></h2>
<h3 id="signed-distance-fields-sdfs" tabindex="-1">Signed Distance Fields (SDFs) <a class="header-anchor" href="#signed-distance-fields-sdfs" aria-label="Permalink to &quot;Signed Distance Fields (SDFs)&quot;"></a></h3>
<p>A signed distance field stores, at every point in 3D space, the distance to the nearest surface. Positive values are outside, negative values are inside, and the zero-crossing is the surface itself. SDFs represent arbitrary 3D shapes including caves, arches, and floating geometry.</p>
<p>To render an SDF as a mesh, you run marching cubes (or a variant) to extract the zero-crossing as triangles. The mesh resolution depends on the grid resolution: a 256x256x256 SDF grid produces terrain covering roughly one chunk at 1-meter resolution.</p>
<p><strong>Browser implementation:</strong> WebGPU marching cubes runs entirely on the GPU via compute shaders. Will Usher's <a href="https://www.willusher.io/webgpu-marching-cubes/" target="_blank" rel="noreferrer">webgpu-marching-cubes</a> implementation processes a 256^3 grid in real time in the browser, achieving native-speed performance. The algorithm is embarrassingly parallel (each cell processes independently), making it ideal for GPU compute.</p>
<div class="language-wgsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">wgsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">workgroup_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> marchingCubes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builtin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(global_invocation_id) id: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">u32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sdfValues </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sampleSDF</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> caseIndex </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> classifyCell</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(sdfValues);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (caseIndex </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0u</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ||</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> caseIndex </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 255u</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) { </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> triangles </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lookupTriangulation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(caseIndex);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> vertices </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> interpolateEdges</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(sdfValues, triangles);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    appendToMeshBuffer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(vertices);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p><strong>Storage cost:</strong> A 256^3 SDF at 8-bit quantized precision is 16 MB uncompressed. But most of the volume is empty (far from the surface). Run-length encoding or sparse octree storage typically reduces this to 100-500 KB per chunk, comparable to heightmap terrain.</p>
<p><strong>Editing:</strong> SDF terrain is naturally editable. Adding material is a <code>min()</code> operation on the distance field. Removing material (digging) is a <code>max()</code> with a negated shape. Smooth blending between shapes uses <code>smoothMin()</code>. These operations run in a compute shader at interactive rates.</p>
<h3 id="dual-contouring" tabindex="-1">Dual Contouring <a class="header-anchor" href="#dual-contouring" aria-label="Permalink to &quot;Dual Contouring&quot;"></a></h3>
<p>Marching cubes places vertices on grid edges, which produces smooth surfaces but loses sharp features (cliff edges, rock corners). Dual contouring places one vertex per cell at the position that best represents the surface within that cell, preserving sharp edges and corners.</p>
<p>The algorithm needs both the distance field values and the surface normals (the gradient of the SDF) at each grid point. It solves a small least-squares problem per cell to find the optimal vertex position. The result is a mesh that captures both smooth terrain and sharp rocky features.</p>
<p><strong>Neural Dual Contouring</strong> (Chen et al., 2022, <a href="https://arxiv.org/abs/2202.01999" target="_blank" rel="noreferrer">arXiv:2202.01999</a>) replaces the least-squares solver with a neural network that predicts optimal vertex positions and edge crossings. It achieves better surface reconstruction accuracy and feature preservation, particularly for complex natural rock formations.</p>
<h3 id="the-transvoxel-algorithm" tabindex="-1">The Transvoxel Algorithm <a class="header-anchor" href="#the-transvoxel-algorithm" aria-label="Permalink to &quot;The Transvoxel Algorithm&quot;"></a></h3>
<p>The hardest problem in volumetric terrain isn't generating the mesh. It's LOD transitions. When a high-resolution chunk sits next to a low-resolution chunk, the meshes don't line up at the boundary, creating visible cracks.</p>
<p>The Transvoxel algorithm, designed by Eric Lengyel (<a href="https://transvoxel.org/" target="_blank" rel="noreferrer">transvoxel.org</a>), solves this by inserting special transition cells along boundaries between resolutions. These cells bridge the resolution difference with additional triangles that exactly match both sides. The algorithm reduces the complex boundary problem to 73 equivalence classes (compared to ~1.2 million cases for a brute-force approach).</p>
<p>Transvoxel is specifically designed for real-time applications where voxel data changes dynamically (creator edits, erosion, mining). It's patent-free and has been used in shipped games (Space Engineers, Astroneer). For a browser world with editable volumetric terrain, Transvoxel is the LOD solution.</p>
<h3 id="hybrid-heightmap-base-volumetric-overlays" tabindex="-1">Hybrid: Heightmap Base + Volumetric Overlays <a class="header-anchor" href="#hybrid-heightmap-base-volumetric-overlays" aria-label="Permalink to &quot;Hybrid: Heightmap Base + Volumetric Overlays&quot;"></a></h3>
<p>The practical approach for a browser open world is a layered system:</p>
<p><strong>Layer 1: Heightmap terrain</strong> covers the entire world. This is the cheap, compact representation for rolling hills, valleys, and mountains. It streams as small heightmap patches per chunk (2-4 KB each). Rendering uses geometry clipmaps with constant GPU cost.</p>
<p><strong>Layer 2: Volumetric overlays</strong> exist only in chunks where complex geometry is needed. Caves, cliff faces, arches, creator-carved tunnels, and underground spaces are stored as sparse SDF volumes. Only chunks with volumetric data incur the SDF storage and marching cubes cost.</p>
<p><strong>Layer 3: Creator modifications</strong> are stored as SDF edits on top of the base layers. A creator who digs a tunnel stores the tunnel's SDF shape. The rendering system combines the heightmap surface with volumetric subtractions and additions to produce the final mesh.</p>
<p>This hybrid costs almost nothing for flat terrain (just the heightmap), and scales only where complexity exists. In a typical world, maybe 5-10% of chunks need volumetric data.</p>
<h2 id="physically-correct-terrain-generation" tabindex="-1">Physically Correct Terrain Generation <a class="header-anchor" href="#physically-correct-terrain-generation" aria-label="Permalink to &quot;Physically Correct Terrain Generation&quot;"></a></h2>
<p>Noise produces random terrain. Physics produces realistic terrain. The difference is visible: noise terrain has no structure (it's randomly bumpy everywhere), while physical terrain has river networks, ridge lines, alluvial fans, and cliff bands that emerge from erosion and tectonics.</p>
<h3 id="hydraulic-erosion-the-foundation" tabindex="-1">Hydraulic Erosion: The Foundation <a class="header-anchor" href="#hydraulic-erosion-the-foundation" aria-label="Permalink to &quot;Hydraulic Erosion: The Foundation&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/eaXk97ujbPQ" title="Coding Adventure: Hydraulic Erosion" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Sebastian Lague's hydraulic erosion implementation in action. Simulated raindrops flow downhill, erode material based on speed and slope, and deposit sediment when velocity drops — transforming flat noise into river valleys, ridgelines, and alluvial fans.</div>
<p>Water flows downhill, picks up sediment, and deposits it when it slows down. This single process, simulated over millions of virtual years, transforms featureless noise into terrain with recognizable geological features.</p>
<p>The particle-based approach drops simulated raindrops on the heightmap. Each drop flows downhill (following the gradient), erodes material based on speed and slope, carries sediment as dissolved load, and deposits sediment when velocity decreases or capacity is exceeded. After 200,000-500,000 particles, the terrain develops:</p>
<ul>
<li><strong>River valleys</strong> that follow the path of maximum water flow</li>
<li><strong>Ridge lines</strong> that separate drainage basins</li>
<li><strong>Alluvial fans</strong> where steep valleys open into flat plains</li>
<li><strong>V-shaped valleys</strong> in mountainous terrain, U-shaped in glaciated terrain</li>
</ul>
<p><strong>GPU implementation:</strong> The particle simulation is parallelizable. Each particle is independent (the approximation ignores particle-particle interaction, which is fine for erosion). A WebGPU compute shader processes 10,000 particles per frame at 60fps, completing 200,000 particles in about 3 seconds of real time.</p>
<p>Sebastian Lague's open-source implementation (<a href="https://github.com/SebLague/Hydraulic-Erosion" target="_blank" rel="noreferrer">GitHub</a>) is the standard starting point. It runs on a single thread in C# and processes a 1024x1024 heightmap in a few seconds. The GPU version is 50-100x faster.</p>
<h3 id="thermal-erosion" tabindex="-1">Thermal Erosion <a class="header-anchor" href="#thermal-erosion" aria-label="Permalink to &quot;Thermal Erosion&quot;"></a></h3>
<p>Water isn't the only erosion force. Temperature changes cause rock to crack and crumble (thermal weathering). When the slope between two terrain points exceeds a material's angle of repose, material falls from the higher point to the lower one. This produces:</p>
<ul>
<li><strong>Talus slopes</strong> at the base of cliffs (piles of fallen rock)</li>
<li><strong>Softened ridge lines</strong> over time</li>
<li><strong>Material-dependent profiles</strong> (hard rock maintains steep slopes, soft soil collapses to gentle angles)</li>
</ul>
<p>Thermal erosion is simpler than hydraulic erosion. It's a local operation: for each cell, compare the height difference with neighbors. If the slope exceeds the threshold, move material downhill. It runs as a single compute shader pass per iteration and converges in 50-100 iterations.</p>
<p>Combining hydraulic and thermal erosion produces terrain that looks dramatically more natural than either alone. Water carves valleys; thermal erosion softens the ridges between them and fills the valley floors with debris.</p>
<h3 id="the-stream-power-law-analytical-erosion" tabindex="-1">The Stream Power Law: Analytical Erosion <a class="header-anchor" href="#the-stream-power-law-analytical-erosion" aria-label="Permalink to &quot;The Stream Power Law: Analytical Erosion&quot;"></a></h3>
<p>Recent research offers an alternative to particle-based simulation. The stream power law (a geomorphological equation relating erosion rate to drainage area and slope) can be solved analytically rather than simulated iteratively.</p>
<p>Cordonnier et al. (2024, <a href="https://hal.science/hal-04525371" target="_blank" rel="noreferrer">HAL</a>) combine the analytical stream power law with landslide and hillslope diffusion processes. The result is terrain generation that's physically grounded but runs as a mathematical function rather than a temporal simulation. You input a noise-based heightmap and parameters (rainfall rate, rock hardness, tectonic uplift rate) and get an eroded terrain in milliseconds.</p>
<p>This analytical approach is ideal for a browser world because it runs once server-side during terrain generation, not iteratively. The parameters are tunable by creators (&quot;make this region more mountainous&quot; adjusts the uplift rate; &quot;make it wetter&quot; increases rainfall and deepens valleys).</p>
<h3 id="arenite-multi-physics-erosion-siggraph-2025" tabindex="-1">Arenite: Multi-Physics Erosion (SIGGRAPH 2025) <a class="header-anchor" href="#arenite-multi-physics-erosion-siggraph-2025" aria-label="Permalink to &quot;Arenite: Multi-Physics Erosion (SIGGRAPH 2025)&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/pl_GdrBSAmA" title="Arenite: A Physics-based Sandstone Simulator (SIGGRAPH 2025)" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Arenite simulates stress, wind erosion, fluvial erosion, and particle deposition to generate arches, hoodoos, and buttes from layered rock. Server-side generation produces SDF volumes that feed directly into a marching cubes pipeline.</div>
<p>Arenite (<a href="https://jango6324.github.io/arenite/" target="_blank" rel="noreferrer">Project page</a>) is a physics-based sandstone simulator that generates arches, alcoves, hoodoos, and buttes from simple initial conditions. Users paint erodability maps (soft rock vs. hard rock layers) and vegetation, then the system simulates:</p>
<ul>
<li><strong>Stress distribution</strong> through the rock column</li>
<li><strong>Wind erosion</strong> that preferentially removes exposed soft material</li>
<li><strong>Fluvial erosion</strong> from water flow</li>
<li><strong>Particle deposition</strong> that creates new formations</li>
</ul>
<p>The GPU implementation runs in under 5 minutes for complex formations on a desktop GPU. While this is too slow for real-time browser use, it's fast enough for server-side generation. A creator could define a cliff face with soft/hard rock layers and get a realistic arch formation within minutes.</p>
<p>The output is a 3D voxel field, which fits directly into our SDF/marching cubes pipeline.</p>
<h3 id="flexible-erosion-across-representations" tabindex="-1">Flexible Erosion Across Representations <a class="header-anchor" href="#flexible-erosion-across-representations" aria-label="Permalink to &quot;Flexible Erosion Across Representations&quot;"></a></h3>
<p>A 2024 paper from IRIT-STORM (&quot;Flexible Terrain Erosion,&quot; <a href="https://link.springer.com/article/10.1007/s00371-024-03444-w" target="_blank" rel="noreferrer">Springer</a>) solves a practical problem: most erosion methods only work on heightfields. If your terrain uses voxels, SDFs, or layered materials, you need separate erosion code for each.</p>
<p>The flexible erosion method decomposes erosion into two independent processes: terrain alteration (removing material from the surface) and material transport (moving sediment with particles governed by basic physics). Each particle has configurable size, density, restitution, and sediment capacity. An optional vector field controls particle motion for realistic fluid dynamics.</p>
<p>Because the particles interact with the terrain through a unified material alteration interface, the same simulation works on heightfields, voxel grids, implicit surfaces, and layered material stacks. For our hybrid terrain (heightmap base + SDF overlays), this means one erosion system handles both representations. The particle simulation runs in parallel on the GPU.</p>
<h3 id="river-networks-and-drainage-basins" tabindex="-1">River Networks and Drainage Basins <a class="header-anchor" href="#river-networks-and-drainage-basins" aria-label="Permalink to &quot;River Networks and Drainage Basins&quot;"></a></h3>
<p>Erosion carves rivers, but generating convincing river networks requires more than just running water downhill. Two approaches produce better results:</p>
<p><strong>Drainage-first generation</strong> (Amit Patel, Red Blob Games, <a href="https://www.redblobgames.com/x/1723-procedural-river-growing/" target="_blank" rel="noreferrer">Project</a>) builds the river network before assigning elevation. Start with a graph (Voronoi or triangle mesh). Classify edges as ridges (no flow), entries (water flows in), or outlets (water flows out). This creates realistic drainage hierarchies where rivers converge from small tributaries into major waterways, following the Rosgen classification system for different river types (braided channels in flat terrain, narrow gorges in mountains).</p>
<p><strong>Flow accumulation</strong> tracks how much water passes through each terrain cell. Drop simulated rain uniformly, flow it downhill, and count visits per cell. Cells with high accumulation are river channels. Cells with moderate accumulation are seasonal streams. The accumulation map also drives erosion intensity (more water = more erosion) and vegetation distribution (river banks are wetter, supporting different plant species).</p>
<p>For a creator world, the river network is generated server-side during terrain creation and stored as a 2D flow direction map plus a water accumulation map per chunk. The browser client uses these maps to render water surfaces (flat planes at the correct height in river channels) and to drive the vegetation distribution (lusher near water).</p>
<h3 id="coastal-and-shoreline-generation" tabindex="-1">Coastal and Shoreline Generation <a class="header-anchor" href="#coastal-and-shoreline-generation" aria-label="Permalink to &quot;Coastal and Shoreline Generation&quot;"></a></h3>
<p>Coastlines are where terrain meets water, and they have distinctive features that standard erosion doesn't produce: sea cliffs, beach deposits, tidal flats, sea stacks, and wave-cut platforms.</p>
<p>NEWTS1.0 (2024, <a href="https://dspace.mit.edu/handle/1721.1/157677" target="_blank" rel="noreferrer">MIT</a>) models rocky coastline evolution using two erosion mechanisms: uniform retreat (constant erosion rate) and wave-driven erosion (erosion rate as a function of fetch distance and incident wave angle). The model runs over thousands of simulated years and produces headlands, bays, sea stacks, and arches that match real coastal geomorphology.</p>
<p>For a browser world, coastal features would be pre-generated during world creation. The parameters (dominant wave direction, rock hardness variation along the coast) let creators control the character of their coastline. A &quot;Norwegian fjord&quot; setting produces steep-sided inlets. A &quot;tropical atoll&quot; setting produces low-lying sandy shores with lagoons.</p>
<h3 id="cave-and-underground-generation" tabindex="-1">Cave and Underground Generation <a class="header-anchor" href="#cave-and-underground-generation" aria-label="Permalink to &quot;Cave and Underground Generation&quot;"></a></h3>
<p>Caves require fully volumetric terrain (SDFs or voxels) since heightmaps can't represent enclosed spaces. The generation approaches:</p>
<p><strong>3D noise thresholding</strong> is the simplest method. Sample 3D Perlin or simplex noise at each voxel. Values below a threshold are solid, above are empty. Adjust the threshold and noise parameters to control tunnel diameter, connectivity, and chamber size. This produces organic, worm-like cave systems reminiscent of Minecraft.</p>
<p><strong>PLUME (Procedural Layer Underground Modeling Engine)</strong> (2024, <a href="https://arxiv.org/html/2508.20926v1" target="_blank" rel="noreferrer">arXiv:2508.20926</a>) generates realistic cave and lava tube environments using layered procedural rules. Originally built for space exploration research (simulating Martian lava tubes), it produces geologically plausible underground structures with stalactites, columns, and chamber systems.</p>
<p><strong>L-system tunnels with metaball carving</strong> use an L-system grammar to grow branching tunnel paths through the terrain, then carve the actual tunnel geometry using metaball implicit surfaces. The metaballs produce smooth, rounded cave walls. Multiple passes with different parameters create primary passages, side chambers, and narrow connecting tunnels.</p>
<p>For a creator world, cave generation ties into the SDF overlay system. The base terrain is a heightmap (no caves). When a chunk needs caves (either from procedural generation or creator design), an SDF volume is generated that subtracts cave geometry from the base terrain. The marching cubes pipeline renders the combined surface.</p>
<h3 id="vegetation-as-a-physical-process" tabindex="-1">Vegetation as a Physical Process <a class="header-anchor" href="#vegetation-as-a-physical-process" aria-label="Permalink to &quot;Vegetation as a Physical Process&quot;"></a></h3>
<p>Vegetation in games is typically placed procedurally with rules like &quot;grass below 2000m, trees below 1500m, snow above 3000m.&quot; This is fast but produces uniform, unrealistic distribution.</p>
<p>Physically grounded vegetation simulation models each plant as competing for resources (light, water, soil nutrients). The simulation:</p>
<ol>
<li>Seeds are scattered across the terrain</li>
<li>Each plant grows based on available resources (water flows from the hydraulic erosion simulation, sunlight depends on slope and aspect, soil depth depends on erosion history)</li>
<li>Plants compete: trees shade out grass, dense canopy prevents new seedlings</li>
<li>Over simulated time, biomes emerge naturally: forests in valleys with water, sparse vegetation on windswept ridges, wetlands where water pools</li>
</ol>
<p>Deussen et al.'s ecosystem simulation (<a href="https://export.arxiv.org/pdf/2208.01471v1.pdf" target="_blank" rel="noreferrer">Paper</a>) generates forest distributions that match real-world ecological patterns. The simulation runs on a 2D grid (one cell per terrain chunk) and produces density maps and species assignments that the rendering system uses for GPU instanced vegetation placement.</p>
<p>For a browser world, the vegetation simulation runs once during world generation (server-side). The output is a set of density maps per chunk: tree density, grass density, flower density, rock debris density. The browser client uses these maps with GPU instancing to scatter vegetation at runtime.</p>
<h2 id="diffusion-based-terrain-synthesis" tabindex="-1">Diffusion-Based Terrain Synthesis <a class="header-anchor" href="#diffusion-based-terrain-synthesis" aria-label="Permalink to &quot;Diffusion-Based Terrain Synthesis&quot;"></a></h2>
<p>This is where the field is moving fastest. Diffusion models (the same technology behind Stable Diffusion for images) are being applied to terrain generation, and the results are substantially more realistic than noise-based methods.</p>
<h3 id="terrain-diffusion-a-successor-to-perlin-noise" tabindex="-1">Terrain Diffusion: A Successor to Perlin Noise <a class="header-anchor" href="#terrain-diffusion-a-successor-to-perlin-noise" aria-label="Permalink to &quot;Terrain Diffusion: A Successor to Perlin Noise&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/hJoaCZ7wZ5s" title="Terrain Diffusion Minecraft Integration" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Terrain Diffusion running inside Minecraft, streaming infinite seed-consistent terrain in real time. Trained on real-world elevation data, it produces landscapes with geological structure that noise functions can't match.</div>
<p>Terrain Diffusion (Goslin, 2025, <a href="https://arxiv.org/abs/2512.08309" target="_blank" rel="noreferrer">arXiv:2512.08309</a>, <a href="https://xandergos.github.io/terrain-diffusion/" target="_blank" rel="noreferrer">Project page</a>) is the most significant advance in procedural terrain generation since Perlin noise in 1985. The work has since been accepted to SIGGRAPH 2026, and the canonical arXiv title is now &quot;InfiniteDiffusion: Bridging Learned Fidelity and Procedural Utility for Open-World Terrain Generation.&quot; The InfiniteDiffusion algorithm and the Terrain Diffusion framework are the two halves of the same paper.</p>
<p>The key innovation is <strong>InfiniteDiffusion</strong>, an algorithm that reformulates diffusion sampling for unbounded domains. Traditional diffusion models generate fixed-size outputs (e.g., a 512x512 heightmap). InfiniteDiffusion generates terrain of infinite extent with:</p>
<ul>
<li><strong>Seed consistency:</strong> Same seed always produces the same terrain, like Perlin noise</li>
<li><strong>Constant-time random access:</strong> You can query the height at any point without generating neighboring regions first</li>
<li><strong>No boundary artifacts:</strong> Infinite generation without visible seams or repetition</li>
</ul>
<p>The system uses a hierarchical stack of diffusion models. The top level captures planetary-scale features (continents, mountain ranges). Each subsequent level adds finer detail (individual peaks, valleys, small-scale roughness). A compact Laplacian encoding stabilizes outputs across the enormous dynamic range from sea level to Himalayan peaks.</p>
<p><strong>Performance:</strong> Generation keeps pace with real-time exploration. The paper reports that even at the theoretical extreme of orbital velocity (about 7,700 m/s), terrain synthesis runs 9x faster than traversal on a consumer GPU. The project includes a Minecraft integration demonstrating real-time terrain synthesis.</p>
<p><strong>Why this matters for us:</strong> Terrain Diffusion produces landscapes trained on real-world elevation data (Earth's actual topography). The output has river networks, mountain ranges, coastal features, and plateau structures that emerge from the training data, not from hand-tuned noise parameters. A creator could say &quot;generate terrain like the Scottish Highlands&quot; and the diffusion model would produce something with the right geological character.</p>
<p>The model runs server-side during world generation. The output is a standard heightmap that streams to the browser like any other terrain data. The generation method is invisible to the client.</p>
<h3 id="terrafusion-joint-geometry-and-texture" tabindex="-1">TerraFusion: Joint Geometry and Texture <a class="header-anchor" href="#terrafusion-joint-geometry-and-texture" aria-label="Permalink to &quot;TerraFusion: Joint Geometry and Texture&quot;"></a></h3>
<div class="img-embed">
<img src="https://cdn.cinevva.com/blog/terrafusion-teaser.png" alt="TerraFusion jointly generates terrain heightmaps and matching surface textures from sketches using latent diffusion" />
</div>
<div class="game-caption">TerraFusion generates heightmap and texture together from a hand-drawn sketch. The correlation between geometry and material is baked in — river beds come out sandy, cliff faces rocky, flat ground grassy — without any manual splat painting.</div>
<p>TerraFusion (2025, <a href="https://arxiv.org/abs/2505.04050" target="_blank" rel="noreferrer">arXiv:2505.04050</a>) goes further by jointly generating heightmaps and terrain textures. The key insight: terrain geometry and surface appearance are correlated (river beds are sandy, cliff faces are rocky, flat areas are grassy). Generating them separately produces mismatches.</p>
<p>TerraFusion uses a latent diffusion model with separate VAEs for heightmaps and textures, trained to model their joint distribution. The system supports:</p>
<ul>
<li><strong>Unconditional generation:</strong> Random plausible terrain with matching textures</li>
<li><strong>Sketch-conditioned generation:</strong> A creator draws a rough map (valleys here, ridges there, cliffs along this edge) and the model generates detailed geometry and textures that match the sketch</li>
</ul>
<p>For a creator world, this is powerful. A creator sketches the general layout of their plot, and the system fills in geologically plausible terrain with appropriate surface materials. No heightmap painting, no texture splatting, no manual material assignment.</p>
<h3 id="mesa-text-to-terrain" tabindex="-1">MESA: Text-to-Terrain <a class="header-anchor" href="#mesa-text-to-terrain" aria-label="Permalink to &quot;MESA: Text-to-Terrain&quot;"></a></h3>
<div class="img-embed">
<img src="https://cdn.cinevva.com/blog/mesa-header.png" alt="MESA generates satellite-style terrain images and elevation maps from text prompts, trained on global Copernicus data" />
</div>
<div class="game-caption">MESA outputs paired satellite images and DEMs from text prompts. Trained on the Copernicus global dataset, it understands real terrain at every scale and climate zone — fjords, steppes, farmland, alpine ridges.</div>
<p>MESA (2025, <a href="https://arxiv.org/abs/2504.07210" target="_blank" rel="noreferrer">arXiv:2504.07210</a>, CVPR 2025 Workshop) generates terrain from text descriptions. It's trained on global remote sensing data from the Copernicus program, so it has exposure to every type of terrestrial landscape.</p>
<p>A prompt like &quot;a fjord with steep granite walls opening to a rocky coastline&quot; produces a heightmap with the appropriate geological structure. &quot;Rolling farmland with gentle hills and a wide river valley&quot; produces something completely different.</p>
<p>MESA introduces the Major TOM Core-DEM extension dataset, which pairs satellite imagery with digital elevation models globally. This training data gives the model understanding of how real terrain looks at every scale and in every climate zone.</p>
<h3 id="geodiffussr-text-guided-terrain-texturing" tabindex="-1">Geodiffussr: Text-Guided Terrain Texturing <a class="header-anchor" href="#geodiffussr-text-guided-terrain-texturing" aria-label="Permalink to &quot;Geodiffussr: Text-Guided Terrain Texturing&quot;"></a></h3>
<div class="img-embed">
<img src="https://cdn.cinevva.com/blog/geodiffussr-teaser.png" alt="Geodiffussr generates terrain textures from text descriptions while respecting elevation data — snow on peaks, sand at coasts, forest on slopes" />
</div>
<div class="game-caption">Geodiffussr takes a heightmap and a text prompt and generates surface textures that respect the elevation. Snow only appears above plausible altitudes; vegetation thins with height; water sits in depressions. The geometry stays unchanged.</div>
<p>Geodiffussr (2025, <a href="https://arxiv.org/abs/2511.23029" target="_blank" rel="noreferrer">arXiv:2511.23029</a>) takes an existing heightmap and generates textures guided by text descriptions while respecting the elevation data. &quot;Autumn forest&quot; produces orange and gold foliage on moderate slopes with bare rock on steep faces. &quot;Tropical coast&quot; produces palm vegetation on low ground with coral sand at sea level.</p>
<p>The system uses multi-scale content aggregation to ensure texture assignments respect elevation: snow only appears above a physically plausible altitude, water features sit in depressions, vegetation thins with altitude.</p>
<p>For a creator world, this means terrain textures can be regenerated from a text prompt without changing the geometry. A creator sculpts the terrain they want, then describes the mood (&quot;dark volcanic wasteland&quot; or &quot;lush temperate forest&quot;) and the system generates appropriate surface materials.</p>
<h2 id="dynamic-lod-for-browser-terrain" tabindex="-1">Dynamic LOD for Browser Terrain <a class="header-anchor" href="#dynamic-lod-for-browser-terrain" aria-label="Permalink to &quot;Dynamic LOD for Browser Terrain&quot;"></a></h2>
<p>Rendering large terrain at full resolution everywhere is impossible in a browser. A 4 km x 4 km world at 1-meter resolution is 16 million vertices of terrain alone. Dynamic LOD reduces this to a constant, manageable vertex count regardless of world size.</p>
<h3 id="geometry-clipmaps" tabindex="-1">Geometry Clipmaps <a class="header-anchor" href="#geometry-clipmaps" aria-label="Permalink to &quot;Geometry Clipmaps&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/yoUQRT-Hmcc" title="Geometry clipmaps: Terrain rendering using nested regular grids (SIGGRAPH 2004)" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">The original SIGGRAPH 2004 presentation of geometry clipmaps. Concentric rings of terrain at halving resolutions keep vertex count constant regardless of world size — still the foundation of most browser terrain LOD today.</div>
<p>Geometry clipmaps (Losasso and Hoppe, SIGGRAPH 2004, <a href="https://hhoppe.com/geomclipmap.pdf" target="_blank" rel="noreferrer">Paper</a>) remain the gold standard for heightmap terrain LOD. The idea: render terrain as a set of concentric square rings centered on the camera. Each ring is twice the area of the previous one but at half the resolution.</p>
<p>Close to the camera (innermost ring): full resolution, 1-meter grid spacing.
One ring out: 2-meter spacing, covering 4x the area.
Next ring: 4-meter spacing, covering 16x the area.
And so on for 6-8 levels, until the outermost ring covers the entire visible distance.</p>
<p>The total vertex count is constant: roughly N^2 * levels, where N is the ring width in vertices. With N=256 and 8 levels, that's about 500K vertices total. This renders the same whether the world is 1 km or 100 km wide.</p>
<p><strong>Browser implementation:</strong> Geometry clipmaps work in WebGL 2 because they only require standard vertex buffer updates (no compute shaders). As the camera moves, the CPU updates the heightmap data for each ring by sampling the terrain at the appropriate resolution. The vertex shader reads height values from a texture and displaces the flat grid.</p>
<p><strong>Morphing:</strong> The transition between LOD levels produces visible &quot;popping&quot; if handled naively. Geomorphing (detailed in its own section below) blends vertex positions between levels over a transition zone in the vertex shader, producing smooth, pop-free transitions at no extra draw call cost.</p>
<h3 id="cdlod-quadtree-adaptive-clipmaps" tabindex="-1">CDLOD: Quadtree-Adaptive Clipmaps <a class="header-anchor" href="#cdlod-quadtree-adaptive-clipmaps" aria-label="Permalink to &quot;CDLOD: Quadtree-Adaptive Clipmaps&quot;"></a></h3>
<p>CDLOD (Strugar, 2014, <a href="https://www.vertexasylum.com/CDLOD/cdlod_latest.pdf" target="_blank" rel="noreferrer">Paper</a>) improves on geometry clipmaps by using a quadtree instead of fixed concentric rings. The quadtree adapts to the terrain: flat areas use coarse nodes, while areas with high detail (cliffs, ridges) get finer subdivision.</p>
<p>This matters for a creator world because different chunks have different complexity. A flat meadow needs minimal resolution. A mountainous region with cliffs and caves needs maximum detail. CDLOD allocates resolution where it matters.</p>
<p>The CPU-side quadtree traversal is lightweight (a few hundred nodes) and determines which terrain patches to draw at which resolution. The GPU renders each patch as an instanced grid with per-patch LOD uniforms.</p>
<h3 id="concurrent-binary-trees-planetary-scale-tessellation" tabindex="-1">Concurrent Binary Trees: Planetary-Scale Tessellation <a class="header-anchor" href="#concurrent-binary-trees-planetary-scale-tessellation" aria-label="Permalink to &quot;Concurrent Binary Trees: Planetary-Scale Tessellation&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/0TzgFwDmbGg" title="Experimenting with Concurrent Binary Trees for Large Scale Terrain Rendering" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">CBT adaptive tessellation for planetary-scale terrain: triangles subdivide near the camera and merge in the distance, entirely on the GPU. Under 0.2ms on console hardware.</div>
<p>Concurrent Binary Trees (CBT) are a GPU-friendly data structure for adaptive terrain tessellation, presented by Benyoub and Dupuy (Intel, HPG 2024, <a href="https://arxiv.org/html/2407.02215v1" target="_blank" rel="noreferrer">Paper</a>, <a href="https://github.com/AnisB/large_cbt" target="_blank" rel="noreferrer">GitHub</a>).</p>
<p>The core idea: represent the terrain as a binary tree where each node is a triangle. Adaptive subdivision splits triangles that are close to the camera and merges triangles that are far away. The binary tree lives entirely in GPU memory as a 1D array (a binary heap), and subdivision/merge operations run as compute shaders.</p>
<p>The 2024 paper extends CBT from square heightmap domains to arbitrary polygon meshes. This means you can tessellate a sphere (for planetary rendering) or an arbitrary base mesh (for a game world with non-rectangular boundaries). The key improvement: using CBT as a memory pool manager instead of implicit encoding allows much higher subdivision levels.</p>
<p>Performance: planetary-scale terrain tessellation in under 0.2ms on console-level hardware. The algorithm scales linearly with processor count. For WebGPU, this provides a path to planet-scale terrain rendering in a browser, though the implementation complexity is high.</p>
<h3 id="gpu-driven-lod-with-webgpu" tabindex="-1">GPU-Driven LOD with WebGPU <a class="header-anchor" href="#gpu-driven-lod-with-webgpu" aria-label="Permalink to &quot;GPU-Driven LOD with WebGPU&quot;"></a></h3>
<p>WebGPU enables a fully GPU-driven terrain pipeline that eliminates CPU involvement in LOD decisions:</p>
<ol>
<li>
<p><strong>Compute pass 1: Frustum and occlusion culling.</strong> A compute shader tests each terrain patch's bounding box against the view frustum and an occlusion buffer (the depth buffer from the previous frame, downsampled). Invisible patches are discarded entirely.</p>
</li>
<li>
<p><strong>Compute pass 2: LOD selection.</strong> For visible patches, compute the screen-space size and select the appropriate LOD level. Write the LOD level and patch ID into an indirect draw buffer.</p>
</li>
<li>
<p><strong>Compute pass 3: Mesh generation (for volumetric terrain).</strong> For chunks with SDF data, run marching cubes to generate the mesh at the selected LOD resolution.</p>
</li>
<li>
<p><strong>Indirect draw.</strong> A single <code>drawIndexedIndirect()</code> call renders all terrain patches. The GPU decides everything: what to draw, at what resolution, in what order.</p>
</li>
</ol>
<p>This pipeline has constant CPU cost (dispatching compute shaders and the indirect draw call) regardless of world size or complexity. The GPU handles all the per-patch decisions.</p>
<div class="language-wgsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">wgsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">workgroup_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lodSelection</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builtin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(global_invocation_id) id: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">u32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> patchIdx </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">x;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> bounds </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> patchBounds[patchIdx];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">frustumTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(bounds, viewProjection)) { </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">occlusionTest</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(bounds, depthPyramid) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">==</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> OCCLUDED</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) { </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> screenSize </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> projectedSize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(bounds, viewProjection, screenDimensions);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> lod </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> clamp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">u32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(maxScreenSize </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> screenSize)), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0u</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, MAX_LOD);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> drawIdx </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> atomicAdd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">drawCount, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1u</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    drawArgs[drawIdx] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DrawArgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(patchIdx, lod, indexCount[lod], indexOffset[lod]);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="geomorphing-pop-free-lod-transitions" tabindex="-1">Geomorphing: Pop-Free LOD Transitions <a class="header-anchor" href="#geomorphing-pop-free-lod-transitions" aria-label="Permalink to &quot;Geomorphing: Pop-Free LOD Transitions&quot;"></a></h3>
<p>The biggest visual artifact in terrain LOD is popping: vertices suddenly jump to new positions when a patch switches LOD level. Geomorphing eliminates this by smoothly interpolating vertex positions between LOD levels over a transition zone.</p>
<p>The implementation lives entirely in the vertex shader. Each vertex stores both its current-LOD position and its next-coarser-LOD position. As the camera distance crosses the transition threshold, a morph factor blends between the two:</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> morphFactor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(lodNear, lodFar, distanceToCamera);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> morphedHeight </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(fineLodHeight, coarseLodHeight, morphFactor);</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">gl_Position</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> viewProjection </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(worldPos.x, morphedHeight, worldPos.z, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><p>Hoppe's geometry clipmap paper (GPU Gems 2, <a href="https://developer.nvidia.com/gpugems/gpugems2/part-i-geometric-complexity/chapter-2-terrain-rendering-using-gpu-based-geometry" target="_blank" rel="noreferrer">Chapter 2</a>) describes the full implementation for clipmap rings. The morph zone is the outer 20% of each ring. Within this zone, vertices smoothly converge to the next ring's resolution. The visual effect: terrain geometry &quot;melts&quot; between detail levels rather than snapping. At typical camera speeds, the transition is invisible.</p>
<p>Image-space blending (Scherzer et al., <a href="https://onlinelibrary.wiley.com/doi/10.1111/j.1467-8659.2007.00943.x" target="_blank" rel="noreferrer">Paper</a>) is an alternative that blends rendered images of two LOD levels in screen space. This handles more extreme LOD differences (e.g., mesh-to-billboard transitions) but costs an extra render pass for the transition zone.</p>
<h3 id="gpu-driven-vegetation-culling" tabindex="-1">GPU-Driven Vegetation Culling <a class="header-anchor" href="#gpu-driven-vegetation-culling" aria-label="Permalink to &quot;GPU-Driven Vegetation Culling&quot;"></a></h3>
<p>Vegetation (trees, grass, rocks) is often the largest source of draw calls in an open world. A naive approach draws every vegetation instance every frame. GPU-driven culling, now available in Three.js via WebGPU, eliminates invisible instances before they reach the rasterizer.</p>
<p>Three.js's <code>ComputeInstanceCulling</code> (<a href="https://www.threejs-blocks.com/docs/ComputeInstanceCulling" target="_blank" rel="noreferrer">Docs</a>) provides frustum and LOD culling for instanced meshes with 10-100x performance gains for large instance counts. The pipeline:</p>
<ol>
<li>A compute shader reads all instance bounding spheres</li>
<li>Tests each against the camera frustum (6-plane test)</li>
<li>Applies distance-based LOD: instances beyond a threshold switch to lower detail or are culled entirely</li>
<li>Surviving instances are compacted into a buffer and drawn via <code>drawIndirect</code></li>
</ol>
<p>The CPU does zero per-instance work. After initial setup, the cost is one compute dispatch plus one indirect draw call per vegetation type, regardless of instance count.</p>
<p>For denser vegetation, Three.js's <code>IndirectBatchedMesh</code> (<a href="https://www.threejs-blocks.com/docs/IndirectBatchedMesh" target="_blank" rel="noreferrer">Docs</a>) packs multiple geometry types (trees, bushes, rocks) into a single buffer and draws them with multi-draw indirect. One draw call for all vegetation in a chunk.</p>
<p>Combined with the density-map-based scattering from our vegetation system, this means: the density map generates 50,000 grass blade positions in a compute shader, the culling pass eliminates the 70% that are off-screen or too far away, and one indirect draw call renders the surviving 15,000 blades. Total CPU cost: negligible.</p>
<h3 id="lod-for-volumetric-terrain" tabindex="-1">LOD for Volumetric Terrain <a class="header-anchor" href="#lod-for-volumetric-terrain" aria-label="Permalink to &quot;LOD for Volumetric Terrain&quot;"></a></h3>
<p>Volumetric terrain (SDF + marching cubes) needs its own LOD system because the mesh is generated, not pre-authored. The approach:</p>
<p><strong>Multi-resolution SDF storage.</strong> Store the SDF at multiple resolutions in a mipmap-like hierarchy. Level 0 is full resolution (1-meter voxels). Level 1 is 2-meter voxels (8x less data). Level 2 is 4-meter voxels. At each level, the SDF is downsampled by taking the minimum absolute distance.</p>
<p><strong>LOD-selected marching cubes.</strong> Run marching cubes on the SDF level that matches the desired LOD. Close-up chunks use level 0. Distant chunks use level 2 or 3. The Transvoxel algorithm handles the boundary between different levels.</p>
<p><strong>Caching.</strong> Generated meshes are cached until the SDF changes (creator edit) or the LOD level changes (camera moved significantly). For static terrain, the mesh is generated once and reused.</p>
<h2 id="streaming-architecture-for-terrain" tabindex="-1">Streaming Architecture for Terrain <a class="header-anchor" href="#streaming-architecture-for-terrain" aria-label="Permalink to &quot;Streaming Architecture for Terrain&quot;"></a></h2>
<h3 id="chunk-data-format" tabindex="-1">Chunk Data Format <a class="header-anchor" href="#chunk-data-format" aria-label="Permalink to &quot;Chunk Data Format&quot;"></a></h3>
<p>Each terrain chunk (64x64 meters) streams as a compact binary package:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>ChunkPacket {</span></span>
<span class="line"><span>  header: {</span></span>
<span class="line"><span>    chunkX: i16, chunkZ: i16,</span></span>
<span class="line"><span>    version: u32,</span></span>
<span class="line"><span>    flags: u8  // hasHeightmap | hasVolumetric | hasVegetation</span></span>
<span class="line"><span>  }</span></span>
<span class="line"><span>  heightmap: {</span></span>
<span class="line"><span>    resolution: u8,      // 65x65 for full, 33x33 for half, 17x17 for quarter</span></span>
<span class="line"><span>    quantizedHeights: u16[resolution * resolution],  // delta-encoded, zlib compressed</span></span>
<span class="line"><span>    splatMap: u8[4 * resolution * resolution]         // RGBA blend weights, LZ4 compressed</span></span>
<span class="line"><span>  }</span></span>
<span class="line"><span>  volumetric?: {         // only present if flags.hasVolumetric</span></span>
<span class="line"><span>    sdfResolution: u8,   // typically 32 or 64</span></span>
<span class="line"><span>    sparseOctree: bytes  // run-length encoded sparse SDF</span></span>
<span class="line"><span>  }</span></span>
<span class="line"><span>  vegetation?: {         // only present if flags.hasVegetation</span></span>
<span class="line"><span>    treeDensityMap: u8[16 * 16],    // 4m resolution density grid</span></span>
<span class="line"><span>    grassDensityMap: u8[32 * 32],   // 2m resolution density grid</span></span>
<span class="line"><span>    rockDensityMap: u8[16 * 16]</span></span>
<span class="line"><span>  }</span></span>
<span class="line"><span>  creatorObjects: {</span></span>
<span class="line"><span>    count: u16,</span></span>
<span class="line"><span>    objects: PlacedObject[]   // assetId + transform + properties, ~40 bytes each</span></span>
<span class="line"><span>  }</span></span>
<span class="line"><span>}</span></span></code></pre>
</div><p><strong>Typical sizes:</strong></p>
<ul>
<li>Heightmap-only chunk (flat terrain): 2-4 KB compressed</li>
<li>Heightmap + vegetation: 4-8 KB</li>
<li>Heightmap + volumetric + vegetation (complex chunk): 20-100 KB</li>
<li>5x5 full-detail neighborhood: 50-500 KB total</li>
</ul>
<h3 id="progressive-chunk-loading" tabindex="-1">Progressive Chunk Loading <a class="header-anchor" href="#progressive-chunk-loading" aria-label="Permalink to &quot;Progressive Chunk Loading&quot;"></a></h3>
<p>Chunks load in priority order based on distance, direction of movement, and data type:</p>
<p><strong>Priority 1 (immediate, &lt;100ms):</strong> Heightmap geometry for chunks the player is about to enter. The terrain surface appears first. Even at the lowest resolution (17x17 per chunk), the ground is there.</p>
<p><strong>Priority 2 (fast, &lt;300ms):</strong> Splat maps and terrain textures. The ground gets color.</p>
<p><strong>Priority 3 (streaming, &lt;1s):</strong> Full-resolution heightmap upgrade. Vegetation density maps. GPU instancing generates trees and grass.</p>
<p><strong>Priority 4 (background, &lt;3s):</strong> Volumetric SDF data for chunks with caves/overhangs. Marching cubes generates the mesh in a Web Worker, transfers the buffer to the main thread.</p>
<p><strong>Priority 5 (lazy, &lt;10s):</strong> Creator-placed objects. High-resolution textures for structures. Detail objects like flowers, small rocks, debris.</p>
<h3 id="predictive-pre-fetching" tabindex="-1">Predictive Pre-fetching <a class="header-anchor" href="#predictive-pre-fetching" aria-label="Permalink to &quot;Predictive Pre-fetching&quot;"></a></h3>
<p>Don't wait for the player to enter a chunk before loading it. Predict where they're going based on velocity and load ahead:</p>
<ul>
<li><strong>Walking speed (5 km/h):</strong> Pre-fetch 2 chunks ahead (128m). At typical broadband latency, this is 200-400ms of lead time.</li>
<li><strong>Running/riding (15 km/h):</strong> Pre-fetch 4 chunks ahead. The load ring shifts with velocity direction.</li>
<li><strong>Flying/fast travel:</strong> Pause rendering during the transition. Stream the destination chunks at highest priority. Resume rendering when enough data exists for a first frame.</li>
</ul>
<p>The pre-fetch system tracks which chunks are in the cache, which are in-flight (requested but not yet arrived), and which are needed. A priority queue sorts pending requests by urgency. Cancel requests for chunks the player has moved away from.</p>
<h3 id="memory-budget-and-eviction" tabindex="-1">Memory Budget and Eviction <a class="header-anchor" href="#memory-budget-and-eviction" aria-label="Permalink to &quot;Memory Budget and Eviction&quot;"></a></h3>
<p>A browser tab gets 2-4 GB on desktop. The terrain system needs to live within a fraction of that (the rest is for rendering, physics, networking, and the JavaScript heap).</p>
<p><strong>Target budget: 256 MB for all terrain data.</strong></p>
<p>At our typical chunk sizes:</p>
<ul>
<li>Full-detail cached chunks: ~100 (10x10 neighborhood) at 5-100 KB each = 5-10 MB for raw data</li>
<li>GPU terrain geometry: ~50 MB (vertex buffers, index buffers for visible terrain)</li>
<li>Terrain textures: ~100 MB (KTX2 compressed, atlas-packed)</li>
<li>Vegetation instance buffers: ~50 MB (positions, rotations, scales for GPU instancing)</li>
<li>SDF volumes and cached marching cubes meshes: ~50 MB</li>
</ul>
<p>Chunks beyond the visible range are evicted from GPU memory first (textures, vertex buffers), then from CPU cache. The raw heightmap data is the last to go because it's the cheapest to keep and the most important to have when the player turns around.</p>
<h2 id="biome-transitions-and-anti-tiling" tabindex="-1">Biome Transitions and Anti-Tiling <a class="header-anchor" href="#biome-transitions-and-anti-tiling" aria-label="Permalink to &quot;Biome Transitions and Anti-Tiling&quot;"></a></h2>
<h3 id="smooth-biome-boundaries" tabindex="-1">Smooth Biome Boundaries <a class="header-anchor" href="#smooth-biome-boundaries" aria-label="Permalink to &quot;Smooth Biome Boundaries&quot;"></a></h3>
<p>Real landscapes don't have hard edges between biomes. A forest doesn't stop at a line and become desert. There's a gradient: dense forest thins into scattered trees, then scrubland, then sparse desert vegetation. Getting this right makes the world feel continuous rather than tiled.</p>
<p><strong>AutoBiomes</strong> (Kötter et al., <a href="https://cgvr.cs.uni-bremen.de/papers/cgi20/AutoBiomes.pdf" target="_blank" rel="noreferrer">Paper</a>) combines procedural terrain generation with simplified climate simulation. Temperature, humidity, and elevation determine biome type at each point. Between biomes, material weights and vegetation density interpolate over a transition zone (typically 50-100 meters wide). The transition width varies by biome pair: forest-to-grassland is wide and gradual, cliff-to-water is narrow and abrupt.</p>
<p>For a creator world, biome assignment runs on a coarse grid (one biome sample per 16x16 meter area). The terrain shader reads the biome values for the current fragment and its neighbors, interpolates material weights in the transition zone, and blends textures accordingly. Vegetation scattering uses the same interpolated density values, so tree density fades gradually at the forest edge.</p>
<p>Creator control: let creators paint biome overrides on their plots. The system generates default biomes from terrain properties, but creators can override them. Paint &quot;swamp&quot; on a low-lying area and the material shifts to murky water, moss, and dead trees. The biome paint map is a per-chunk 16x16 grid of biome IDs (256 bytes) that overrides the procedural assignment.</p>
<h3 id="hex-tiling-eliminating-texture-repetition" tabindex="-1">Hex-Tiling: Eliminating Texture Repetition <a class="header-anchor" href="#hex-tiling-eliminating-texture-repetition" aria-label="Permalink to &quot;Hex-Tiling: Eliminating Texture Repetition&quot;"></a></h3>
<p>The most common visual artifact in terrain rendering is texture repetition. A 1-meter grass texture tiled across a 100-meter meadow produces visible grid patterns. Two techniques fix this:</p>
<p><strong>Hex-tiling</strong> (Mikkelsen, <a href="https://github.com/mmikk/hextile-demo" target="_blank" rel="noreferrer">Demo</a>) replaces the square tiling grid with a hexagonal one. Each hexagonal tile samples the texture at a random offset and rotation. The hexagonal boundaries are blended to hide seams. The result is a surface that looks uniformly random rather than tiled. Cost: about 3 extra texture samples per fragment. The technique is widely used in production games and runs in any fragment shader.</p>
<p><strong>Stochastic texture filtering</strong> (Pharr et al., NVIDIA, 2024, <a href="https://research.nvidia.com/labs/rtr/publication/pharr2024stochtex/" target="_blank" rel="noreferrer">Paper</a>) applies filtering after shading rather than before, using stochastic sampling. The error from stochastic sampling is minimal and handled well by spatiotemporal denoising. This produces more accurate filtered results and works with compressed/sparse textures. For terrain, it eliminates both tiling artifacts and the filtering artifacts that hex-tiling can sometimes introduce at transitions.</p>
<p>For a browser world, hex-tiling is the practical choice (it works in any shader). Stochastic filtering requires more infrastructure but produces better results if temporal denoising is available (which it would be in a WebGPU path with TAA).</p>
<h2 id="terrain-material-system" tabindex="-1">Terrain Material System <a class="header-anchor" href="#terrain-material-system" aria-label="Permalink to &quot;Terrain Material System&quot;"></a></h2>
<h3 id="triplanar-mapping" tabindex="-1">Triplanar Mapping <a class="header-anchor" href="#triplanar-mapping" aria-label="Permalink to &quot;Triplanar Mapping&quot;"></a></h3>
<p>Standard UV-mapped textures stretch horribly on steep slopes because the UV coordinates compress. Triplanar mapping projects textures along all three axes (X, Y, Z) and blends based on the surface normal:</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> blending </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> abs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(normal);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">blending </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> normalize</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(blending, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.00001</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">blending </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (blending.x </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> blending.y </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> blending.z);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> xaxis </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> texture</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(material, worldPos.yz </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> scale</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> yaxis </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> texture</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(material, worldPos.xz </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> scale</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> zaxis </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> texture</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(material, worldPos.xy </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> scale</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> color </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> xaxis </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> blending.x </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> yaxis </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> blending.y </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> zaxis </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> blending.z;</span></span></code></pre>
</div><p>Cliff faces get the X or Z projection (no stretching). Flat ground gets the Y projection. The blending is smooth and automatic. No UV unwrapping required.</p>
<p>Babylon.js has a built-in <a href="https://doc.babylonjs.com/toolsAndResources/assetLibraries/materialsLibrary/triPlanarMat" target="_blank" rel="noreferrer">TriPlanar Material</a>. Three.js requires a custom shader, but the implementation is about 30 lines of GLSL.</p>
<p>For PBR terrain, apply triplanar mapping to all channels: albedo, normal, roughness, and ambient occlusion. The same blending weights apply to each channel.</p>
<h3 id="slope-and-altitude-based-material-assignment" tabindex="-1">Slope and Altitude-Based Material Assignment <a class="header-anchor" href="#slope-and-altitude-based-material-assignment" aria-label="Permalink to &quot;Slope and Altitude-Based Material Assignment&quot;"></a></h3>
<p>Rather than hand-painting splat maps, assign materials procedurally based on terrain properties:</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> slope </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> acos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(normal, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> altitude </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> worldPos.y;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> grassWeight </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, slope) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2000.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1500.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, altitude);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rockWeight </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, slope);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> snowWeight </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2500.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3000.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, altitude) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, slope);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sandWeight </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">5.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, altitude) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.15</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, slope);</span></span></code></pre>
</div><p>Flat ground at low altitude gets grass. Steep slopes get rock. High altitude gets snow (but only on surfaces flat enough for snow to accumulate). Near sea level gets sand. The transitions are smooth and physically motivated.</p>
<p>For a creator world, expose the altitude thresholds and blend zones as paintable parameters per chunk. Creators can move the treeline up or down, extend the snow coverage, or turn a grassy hill into a sandy desert by adjusting the material rules for their plot.</p>
<h3 id="gpu-friendly-laplacian-texture-blending" tabindex="-1">GPU-Friendly Laplacian Texture Blending <a class="header-anchor" href="#gpu-friendly-laplacian-texture-blending" aria-label="Permalink to &quot;GPU-Friendly Laplacian Texture Blending&quot;"></a></h3>
<p>Standard texture blending (linear interpolation between layers) produces either visible seams or washed-out, low-contrast results. Laplacian pyramid blending solves this but traditionally requires expensive precomputation.</p>
<p>Wronski (NVIDIA, 2025, <a href="https://jcgt.org/published/0014/01/02/" target="_blank" rel="noreferrer">JCGT</a>) presents a GPU-friendly variant that works in real-time shaders with no precomputation and no extra memory. The technique uses the standard mipmap chain as an approximation of the Laplacian pyramid: sample the texture at both the current mip level and a coarser level, compute the difference (the Laplacian), and blend the Laplacian contributions from each layer.</p>
<p>The result preserves sharp local features (individual grass blades, rock cracks) while blending smoothly at larger scales. The cost is a few extra texture taps per fragment. For terrain where you're blending 4+ material layers per pixel, this produces noticeably better results than linear blending, especially at the transitions between grass, rock, and sand.</p>
<h3 id="phasor-noise-for-erosion-detail" tabindex="-1">Phasor Noise for Erosion Detail <a class="header-anchor" href="#phasor-noise-for-erosion-detail" aria-label="Permalink to &quot;Phasor Noise for Erosion Detail&quot;"></a></h3>
<p>Standard terrain often looks flat at close range because the erosion simulation operates at the heightmap resolution (1-meter grid). Real terrain has fine-scale erosion patterns (rills, gullies, weathering cracks) at centimeter scale.</p>
<p>Grenier et al. (2024, <a href="https://diglib.eg.org/bitstream/handle/10.1111/cgf14992/v43i1_05_cgf14992.pdf" target="_blank" rel="noreferrer">CGF</a>) use phasor noise to add terrain micro-detail in real time. Phasor noise synthesizes structured patterns by defining a stochastic phase field fed into periodic functions. Applied to terrain, it creates spatially varying erosion patterns that:</p>
<ul>
<li>Cascade narrow rills into larger gullies across scales</li>
<li>Automatically align with the terrain slope (erosion patterns follow the fall line)</li>
<li>Achieve up to 32x amplification (adding detail at 32x the heightmap resolution)</li>
<li>Run entirely in a fragment shader at interactive frame rates</li>
</ul>
<p>For a browser world, phasor noise runs in the terrain shader as a detail layer. The heightmap provides the large-scale shape. Phasor noise adds convincing micro-erosion in the fragment shader without increasing geometry complexity. The parameters (pattern frequency, amplitude, orientation) can vary per biome: deep rills on bare rock, gentle ripples on sand dunes, rough bark-like texture on dried mud.</p>
<h3 id="virtual-texturing-for-terrain" tabindex="-1">Virtual Texturing for Terrain <a class="header-anchor" href="#virtual-texturing-for-terrain" aria-label="Permalink to &quot;Virtual Texturing for Terrain&quot;"></a></h3>
<p>At large scales, the terrain texture atlas becomes unwieldy. A 4 km x 4 km world at 1 texel per centimeter needs a 400,000 x 400,000 pixel texture. Obviously impossible.</p>
<p>Virtual texturing (also called megatexture, from id Software's Rage) solves this by treating the terrain texture as a paged structure. The full texture exists conceptually but only the tiles visible on screen are loaded into GPU memory.</p>
<p>The pipeline:</p>
<ol>
<li><strong>Feedback pass:</strong> Render the terrain with a shader that outputs which texture tile each pixel needs (tile ID and mip level). Read this back to the CPU (or process with a compute shader).</li>
<li><strong>Tile loading:</strong> Load requested tiles from the CDN or generate them procedurally from the splat map data.</li>
<li><strong>Indirection texture:</strong> A small texture maps virtual tile coordinates to physical tile coordinates in a texture atlas.</li>
<li><strong>Render pass:</strong> The terrain shader looks up the indirection texture to find the correct physical tile, then samples the material texture from the atlas.</li>
</ol>
<p>WebGPU's compute shaders can handle the feedback analysis and page table management entirely on the GPU. The CPU only manages tile I/O.</p>
<p>The result: terrain with unique texturing at any resolution without a texture memory explosion. Tiles far from the camera load at low resolution. Close-up tiles load at high resolution. The total memory usage stays within a fixed budget (typically 128-256 MB of texture atlas).</p>
<h2 id="dynamic-terrain-effects" tabindex="-1">Dynamic Terrain Effects <a class="header-anchor" href="#dynamic-terrain-effects" aria-label="Permalink to &quot;Dynamic Terrain Effects&quot;"></a></h2>
<h3 id="puddles-and-wetness" tabindex="-1">Puddles and Wetness <a class="header-anchor" href="#puddles-and-wetness" aria-label="Permalink to &quot;Puddles and Wetness&quot;"></a></h3>
<p>Rain doesn't just fall. It accumulates. In depressions, it forms puddles. On surfaces, it creates a wet sheen. On steep slopes, it runs off. Simulating this makes weather feel connected to the terrain rather than a purely visual overlay.</p>
<p>The shader-based approach doesn't simulate fluid dynamics. It uses the terrain heightmap to determine where water pools:</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> concavity </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightCenter </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 4.0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> -</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightLeft </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightRight </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightUp </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightDown;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> puddleDepth </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> max</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, concavity </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> rainIntensity </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> evaporationRate </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> timeSinceRain);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> wetness </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> smoothstep</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.02</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, puddleDepth);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> wetColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> wetRoughness </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseRoughness </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> finalColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(baseColor, wetColor, wetness);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> finalRoughness </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(baseRoughness, wetRoughness, wetness);</span></span></code></pre>
</div><p>Concave areas (negative Laplacian of the heightmap) collect water. The deeper the concavity, the larger the puddle. Wet surfaces darken and become more reflective (lower roughness). The effect fades over time after rain stops.</p>
<p>For full puddle reflections, add a planar reflection pass at the puddle surface. Or use screen-space reflections (SSR) which are cheaper and already available in both Three.js and Babylon.js post-processing stacks.</p>
<p>Raindrops on puddle surfaces use a simple 2D wave equation in a feedback texture (Saurel, 2026, <a href="https://www.4rknova.com/blog/2026/02/08/raindrops-on-puddle" target="_blank" rel="noreferrer">Blog</a>). Each raindrop creates a ripple that propagates outward and decays. The wave texture modulates the puddle normal map, creating convincing ripple patterns at 60fps.</p>
<h3 id="footprints-and-terrain-deformation" tabindex="-1">Footprints and Terrain Deformation <a class="header-anchor" href="#footprints-and-terrain-deformation" aria-label="Permalink to &quot;Footprints and Terrain Deformation&quot;"></a></h3>
<p>When a player walks on soft terrain (sand, snow, mud), footprints add to the sense of physical presence. The technique: maintain a small deformation texture per chunk (64x64 pixels = 4 KB) that stores height offsets. When a character steps on soft terrain, stamp a footprint shape into the deformation texture.</p>
<p>The terrain vertex shader reads the deformation texture and subtracts the offset from the height. The terrain fragment shader darkens the footprint area (compressed soil is darker) and increases roughness (disturbed surface).</p>
<p>Footprints fade over time (snow fills in, rain washes out mud prints) by gradually zeroing the deformation texture. The fade rate depends on weather: fast in rain, slow in dry conditions.</p>
<p>For a multiplayer world, footprint data is ephemeral and local. Each client generates footprints for visible players. The deformation texture doesn't need to sync across clients (everyone sees their own version of transient footprints). This avoids the networking cost of broadcasting every step.</p>
<h3 id="procedural-sky-and-day-night-cycle" tabindex="-1">Procedural Sky and Day/Night Cycle <a class="header-anchor" href="#procedural-sky-and-day-night-cycle" aria-label="Permalink to &quot;Procedural Sky and Day/Night Cycle&quot;"></a></h3>
<p>The sky is the largest visible surface in any open world. It sets the mood for the entire terrain.</p>
<p>Three.js has a <a href="https://discourse.threejs.org/t/complete-sky-system-for-three-js-skybox-sun-moon-day-night-cycle-clouds-stars-lensflares/88311" target="_blank" rel="noreferrer">complete sky system</a> that includes procedural sun/moon, day/night cycle, clouds, stars, and lens flares. The built-in Three.js <code>Sky</code> example (also available in <a href="https://threejs.org/examples/webgpu_sky.html" target="_blank" rel="noreferrer">WebGPU</a>) implements Preetham's analytical sky model.</p>
<p>For more physically accurate results, <a href="https://jolifantobambla.github.io/webgpu-sky-atmosphere/" target="_blank" rel="noreferrer">webgpu-sky-atmosphere</a> implements Hillaire's atmosphere model as a WebGPU post-process. It supports multiple scattering phase functions and produces correct aerial perspective (distant terrain appears hazier), sun/sunset colors, and sky gradients from first principles.</p>
<p><a href="https://dev.to/the_lone_engineer/terrainview7-full-scale-planet-rendering-in-webgpu-emscripten-now-with-precomputed-atmospheric-18ik" target="_blank" rel="noreferrer">TerrainView7</a> demonstrates full-scale planet rendering in WebGPU with precomputed atmospheric scattering, proving that physically accurate atmosphere rendering runs in a browser.</p>
<p>For a creator world, the sky parameters (sun position, cloud coverage, haze density) are synchronized across all clients from the server's world clock. The sky shader runs locally on each client, producing consistent lighting across all players.</p>
<h3 id="terrain-lighting-indirect-illumination" tabindex="-1">Terrain Lighting: Indirect Illumination <a class="header-anchor" href="#terrain-lighting-indirect-illumination" aria-label="Permalink to &quot;Terrain Lighting: Indirect Illumination&quot;"></a></h3>
<p>Direct sunlight is handled by shadow maps. But the color and brightness of terrain in shadow (ambient/indirect light) is equally important for visual quality. Pure black shadows look wrong. Shadows should be tinted by sky color (blue on a clear day, grey on an overcast day).</p>
<p>For a browser world, the practical approach:</p>
<p><strong>Screen-space indirect lighting</strong> with visibility bitmask (Jimenez et al., 2023, <a href="https://export.arxiv.org/pdf/2301.11376v2.pdf" target="_blank" rel="noreferrer">Paper</a>) improves on standard SSAO by tracking 32 directional visibility sectors per pixel. This captures not just how occluded a point is, but the directionality of the occlusion. Thin surfaces correctly allow light through from the other side. The result is indirect illumination that reacts to nearby geometry without global illumination infrastructure.</p>
<p>The cost is comparable to SSAO (1-2ms). The visual improvement is significant: terrain in canyons picks up bounce light from the canyon walls. The undersides of overhangs are illuminated by ground reflection. The effect combines with the atmospheric scattering from the sky shader to produce physically motivated ambient lighting.</p>
<h2 id="terrain-physics-integration" tabindex="-1">Terrain Physics Integration <a class="header-anchor" href="#terrain-physics-integration" aria-label="Permalink to &quot;Terrain Physics Integration&quot;"></a></h2>
<h3 id="rapier-heightfield-colliders" tabindex="-1">Rapier Heightfield Colliders <a class="header-anchor" href="#rapier-heightfield-colliders" aria-label="Permalink to &quot;Rapier Heightfield Colliders&quot;"></a></h3>
<p>Rapier's WASM physics engine provides native heightfield colliders optimized for terrain. Creating a terrain collider is straightforward:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> heights</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Float32Array</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">65</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> *</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 65</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Fill with heightmap data...</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> groundCollider</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> RAPIER</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.ColliderDesc.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">heightfield</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">  64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, heights, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> RAPIER</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Vector3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">world.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">createCollider</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(groundCollider);</span></span></code></pre>
</div><p>The heightfield collider uses the structured grid for efficient broadphase queries. Raycasting against a heightfield is O(log n) rather than O(n) for a triangle mesh. For a 65x65 chunk, collision queries resolve in microseconds.</p>
<p>For chunks with volumetric terrain (SDF overlays), generate a triangle mesh from the marching cubes output and use a trimesh collider. This is more expensive than a heightfield collider but handles arbitrary geometry. Only generate trimesh colliders for the 3-5 chunks closest to the player. Distant chunks don't need physics.</p>
<p><strong>Performance:</strong> Rapier's heightfield collider for a 65x65 chunk adds approximately 0.1ms per physics step for character controller queries. 5 active chunks with heightfield colliders: 0.5ms. One trimesh collider for a volumetric chunk: 0.2-0.5ms. Total terrain physics budget: under 1ms, well within the 2-3ms physics budget for the entire world.</p>
<h3 id="terrain-aware-character-controller" tabindex="-1">Terrain-Aware Character Controller <a class="header-anchor" href="#terrain-aware-character-controller" aria-label="Permalink to &quot;Terrain-Aware Character Controller&quot;"></a></h3>
<p>The character controller needs to respond to terrain properties:</p>
<ul>
<li><strong>Slope limiting:</strong> The character can walk on slopes up to 45 degrees. Steeper slopes cause sliding. This uses the terrain normal at the character's position (cheaply computed from the heightmap gradient).</li>
<li><strong>Surface material response:</strong> Walking on rock produces different footstep sounds and movement speed than walking on sand or mud. The terrain's splat map provides the surface material at any point.</li>
<li><strong>Step climbing:</strong> The character can step up ledges up to 0.5 meters. Rapier's <code>KinematicCharacterController</code> handles this automatically with configurable step height.</li>
</ul>
<h2 id="interactive-terrain-editing-in-the-browser" tabindex="-1">Interactive Terrain Editing in the Browser <a class="header-anchor" href="#interactive-terrain-editing-in-the-browser" aria-label="Permalink to &quot;Interactive Terrain Editing in the Browser&quot;"></a></h2>
<p>For a creator world, terrain isn't just generated. It's sculpted, modified, and reshaped by players. The editing tools need to be responsive (instant visual feedback) and networked (other players see changes within seconds).</p>
<h3 id="webgpu-sdf-editor" tabindex="-1">WebGPU SDF Editor <a class="header-anchor" href="#webgpu-sdf-editor" aria-label="Permalink to &quot;WebGPU SDF Editor&quot;"></a></h3>
<p>Reinder Nijhoff's <a href="https://reindernijhoff.net/2026/01/webgpu-sdf-editor-real-time-signed-distance-field-modeling/" target="_blank" rel="noreferrer">WebGPU SDF Editor</a> demonstrates that full-featured SDF modeling works in the browser today. The editor supports:</p>
<ul>
<li><strong>Six primitive shapes</strong> (sphere, box, cone, cylinder, capsule, torus) with position, rotation, and scale</li>
<li><strong>Boolean operations</strong> (union, subtraction, intersection) with smooth blending and configurable blend radii</li>
<li><strong>Hierarchical scene graphs</strong> with groups and nested operations</li>
<li><strong>Real-time rendering</strong> using multiple GPU compute shader stages, octree-based space partitioning across 16,384 grid cells, and surface extraction via marching cubes or surface nets</li>
<li><strong>Temporal anti-aliasing and ambient occlusion</strong> via shadow maps</li>
</ul>
<p>Each primitive is stored as 28 floats (112 bytes) in a single GPU buffer. This compact representation means a complex terrain edit (dozens of SDF primitives defining a cave entrance, an arch, or a carved cliff face) weighs under 5 KB and syncs to other players instantly.</p>
<p>For a creator world, the SDF editing workflow looks like:</p>
<ol>
<li>Creator selects a sculpting tool (add sphere, subtract box, smooth blend)</li>
<li>Click/drag in the world to place and size the SDF primitive</li>
<li>The client immediately runs marching cubes on the modified SDF to update the local mesh (feedback in &lt;16ms)</li>
<li>The SDF edit (primitive type + transform + blend mode, ~100 bytes) is sent to the server</li>
<li>The server validates the edit (within the creator's plot, doesn't intersect protected areas) and broadcasts to nearby players</li>
<li>Other players' clients apply the SDF edit and regenerate their local mesh</li>
</ol>
<p>The total round-trip for an edit to become visible to others: 100-300ms depending on network latency. The creator sees their edit instantly because it's applied locally before server confirmation.</p>
<h3 id="brush-based-heightmap-editing" tabindex="-1">Brush-Based Heightmap Editing <a class="header-anchor" href="#brush-based-heightmap-editing" aria-label="Permalink to &quot;Brush-Based Heightmap Editing&quot;"></a></h3>
<p>For the heightmap layer (the 90% of terrain that doesn't need volumetric features), a simpler editing model works. The creator paints height modifications with a brush:</p>
<ul>
<li><strong>Raise/lower:</strong> Add or subtract height in a radius with falloff</li>
<li><strong>Smooth:</strong> Average heights in a radius, removing sharp features</li>
<li><strong>Flatten:</strong> Set all heights in a radius to a target value</li>
<li><strong>Erosion brush:</strong> Apply a few steps of hydraulic erosion locally in the brush radius</li>
</ul>
<p>The heightmap edit is a delta: a small patch of height changes that overlays the base terrain. The delta patch is tiny (a 32x32 grid of 16-bit height offsets = 2 KB) and syncs to other players as a single message. Multiple delta patches accumulate per chunk and are periodically merged server-side into the chunk's persistent heightmap.</p>
<h3 id="collaborative-editing-constraints" tabindex="-1">Collaborative Editing Constraints <a class="header-anchor" href="#collaborative-editing-constraints" aria-label="Permalink to &quot;Collaborative Editing Constraints&quot;"></a></h3>
<p>When multiple creators edit the same chunk simultaneously, the system needs rules:</p>
<ul>
<li><strong>Spatial locking:</strong> Only one creator can edit a given 8x8 meter sub-region at a time. The lock is acquired when the creator starts an edit stroke and released when they lift the brush. Locks timeout after 10 seconds of inactivity.</li>
<li><strong>Non-overlapping edits:</strong> If two creators edit different parts of the same chunk, both edits apply without conflict (they modify different heightmap cells or SDF regions).</li>
<li><strong>Overlapping edits:</strong> If two creators edit the same location, the server serializes the edits in arrival order. Both clients see the same final result after reconciliation.</li>
</ul>
<p>This is simpler than the full CRDT approach used for placed objects because terrain edits are additive operations on a continuous field (heights, SDF distances) rather than discrete object state.</p>
<h2 id="nanite-style-virtual-geometry-in-webgpu" tabindex="-1">Nanite-Style Virtual Geometry in WebGPU <a class="header-anchor" href="#nanite-style-virtual-geometry-in-webgpu" aria-label="Permalink to &quot;Nanite-Style Virtual Geometry in WebGPU&quot;"></a></h2>
<p>Unreal Engine 5's Nanite renders billions of triangles by building a cluster DAG (directed acyclic graph) at build time and selecting the right LOD per-cluster at runtime based on screen-space error. The entire pipeline runs on the GPU. This approach has been ported to WebGPU.</p>
<h3 id="nanite-webgpu" tabindex="-1">Nanite WebGPU <a class="header-anchor" href="#nanite-webgpu" aria-label="Permalink to &quot;Nanite WebGPU&quot;"></a></h3>
<div class="img-embed">
<img src="https://cdn.cinevva.com/blog/nanite-webgpu-scene.png" alt="Nanite WebGPU rendering a complex multi-object scene in a browser using meshlet LOD hierarchy and software rasterizer" />
</div>
<div class="game-caption">Nanite WebGPU rendering a full scene in Chrome using meshlet LOD, software rasterization in WGSL compute shaders, and per-meshlet frustum and occlusion culling. No native plugin — pure WebGPU.</div>
<p><a href="https://scthe.github.io/nanite-webgpu/" target="_blank" rel="noreferrer">Nanite WebGPU</a> by Scthe (1.1k+ GitHub stars) is a complete browser implementation of Nanite's core architecture:</p>
<ul>
<li><strong>Meshlet LOD hierarchy</strong> built offline using meshoptimizer's cluster generation</li>
<li><strong>Software rasterizer</strong> implemented in WGSL compute shaders (working within WebGPU's constraints where hardware rasterization can't do per-cluster draws efficiently)</li>
<li><strong>Per-instance and per-meshlet culling</strong> using frustum and occlusion tests</li>
<li><strong>Billboard impostors</strong> for extremely distant objects</li>
<li><strong>Texture and per-vertex normal support</strong></li>
</ul>
<p>The pipeline: meshes are split into clusters of ~128 triangles. Neighboring clusters are grouped, and each group is simplified (using meshoptimizer) while preserving shared boundaries. This recurses until the entire mesh collapses to a single cluster. At runtime, a compute shader walks the DAG and selects the coarsest cluster per-group that produces less than 1 pixel of error at the current screen resolution.</p>
<p><strong>THREE-Nanite</strong> is an emerging Three.js implementation achieving 20-40fps on integrated graphics hardware while handling hundreds of thousands of triangles. It demonstrates that Nanite-style rendering is viable even on low-end browser hardware.</p>
<h3 id="meshoptimizer-the-lod-pipeline-foundation" tabindex="-1">meshoptimizer: The LOD Pipeline Foundation <a class="header-anchor" href="#meshoptimizer-the-lod-pipeline-foundation" aria-label="Permalink to &quot;meshoptimizer: The LOD Pipeline Foundation&quot;"></a></h3>
<p><a href="https://meshoptimizer.org/" target="_blank" rel="noreferrer">meshoptimizer</a> (by Arseny Kapoulkine) is the library behind most browser-compatible LOD pipelines. Version 1.0 (2025) provides:</p>
<ul>
<li><strong>Mesh simplification</strong> with error metrics (how much the shape changed, used for LOD selection)</li>
<li><strong>Cluster generation</strong> for Nanite-style meshlet hierarchies</li>
<li><strong>Vertex cache optimization</strong> for GPU-friendly triangle ordering</li>
<li><strong>Overdraw optimization</strong> to reduce pixel shader cost</li>
<li><strong>Vertex quantization and compression</strong> for smaller downloads</li>
</ul>
<p>meshoptimizer 1.0 (released December 2025) ships a new single-header <code>clusterlod.h</code> that implements Nanite-style continuous LOD directly. It builds a hierarchy of clusters that are progressively grouped and simplified, usable as-is or as a reference for a custom pipeline. This is the exact cluster-DAG primitive the terrain pipeline needs for SDF/marching-cubes mesh LOD.</p>
<p>For our terrain pipeline, meshoptimizer processes the marching cubes output from SDF terrain into optimized, clustered meshes with LOD hierarchies. The offline processing runs server-side. The browser receives pre-clustered meshes and performs GPU-driven LOD selection at runtime.</p>
<p>The combination of meshoptimizer for LOD generation and WebGPU compute for runtime selection gives browser terrain the same architectural pattern as Nanite, adapted for web constraints.</p>
<h2 id="terrain-aware-level-design" tabindex="-1">Terrain-Aware Level Design <a class="header-anchor" href="#terrain-aware-level-design" aria-label="Permalink to &quot;Terrain-Aware Level Design&quot;"></a></h2>
<p>Terrain isn't just a surface to walk on. Its shape guides player movement, directs attention, and creates the emotional rhythm of exploration. The best open worlds use terrain as a design tool.</p>
<h3 id="sightlines-and-landmarks" tabindex="-1">Sightlines and Landmarks <a class="header-anchor" href="#sightlines-and-landmarks" aria-label="Permalink to &quot;Sightlines and Landmarks&quot;"></a></h3>
<p>The Level Design Book's <a href="https://book.leveldesignbook.com/process/blockout/wayfinding" target="_blank" rel="noreferrer">wayfinding chapter</a> documents how terrain elevation controls what players see and where they go. A ridge hides what's beyond it, creating curiosity. A valley funnels movement toward its lowest point. A tall landmark (tower, mountain peak, unusual tree) visible from a distance gives players a goal to walk toward.</p>
<p>For a creator world, this means terrain generation should produce natural wayfinding features. Ridge lines should break sightlines, creating &quot;reveal moments&quot; when a player crests a hill and sees a new area. Valleys should converge toward interesting locations. Elevated points should exist where creators can place landmarks visible from far away.</p>
<h3 id="curiosity-driven-exploration" tabindex="-1">Curiosity-Driven Exploration <a class="header-anchor" href="#curiosity-driven-exploration" aria-label="Permalink to &quot;Curiosity-Driven Exploration&quot;"></a></h3>
<p>Research from Purdue University (<a href="https://web.ics.purdue.edu/~cmousas/papers/conf22-FDG-SpatialExploration.pdf" target="_blank" rel="noreferrer">Paper</a>) identifies four spatial exploration triggers:</p>
<ol>
<li><strong>Reaching extreme points</strong> (highest peak, furthest edge, deepest cave). Terrain should have clear extremes that reward reaching them.</li>
<li><strong>Resolving visual obstructions</strong> (what's behind that cliff? inside that cave?). Terrain that blocks the view motivates movement to find what's hidden.</li>
<li><strong>Out-of-place objects</strong> (a structure in the wilderness, a light in the darkness). Creator-placed objects against natural terrain create contrast that draws attention.</li>
<li><strong>Understanding spatial connections</strong> (how does this valley connect to that coast?). Terrain that creates legible geography encourages map-reading and route-planning.</li>
</ol>
<h3 id="plotmap-ai-assisted-poi-placement" tabindex="-1">PlotMap: AI-Assisted POI Placement <a class="header-anchor" href="#plotmap-ai-assisted-poi-placement" aria-label="Permalink to &quot;PlotMap: AI-Assisted POI Placement&quot;"></a></h3>
<p>PlotMap (<a href="https://arxiv.org/html/2309.15242v4" target="_blank" rel="noreferrer">arXiv:2309.15242</a>) automates point-of-interest layout by taking narrative requirements (this quest needs a village near a river, that quest needs a ruin on a hilltop) and finding terrain locations that satisfy the spatial constraints. For a creator world, a similar system could suggest where to place structures based on terrain properties: &quot;this hilltop has good sightlines for a watchtower,&quot; &quot;this sheltered valley would suit a village.&quot;</p>
<h2 id="flowing-water-and-waterfalls" tabindex="-1">Flowing Water and Waterfalls <a class="header-anchor" href="#flowing-water-and-waterfalls" aria-label="Permalink to &quot;Flowing Water and Waterfalls&quot;"></a></h2>
<p>Rivers and waterfalls are terrain features that combine visual appeal with ambient sound and gameplay affordance (water as a barrier, a resource, or a path).</p>
<h3 id="river-rendering" tabindex="-1">River Rendering <a class="header-anchor" href="#river-rendering" aria-label="Permalink to &quot;River Rendering&quot;"></a></h3>
<p>Rivers in open worlds are typically rendered as textured strips that follow the terrain surface. The strip mesh is generated from the river spline (stored as control points) and projected onto the terrain heightmap. The river shader applies:</p>
<ul>
<li><strong>Flow-aligned UVs</strong> that scroll in the river's direction, creating the appearance of flowing water</li>
<li><strong>Foam at edges</strong> where the river meets the bank (depth-based, similar to shoreline foam)</li>
<li><strong>Speed variation</strong> based on channel width (narrow sections flow faster, wide sections slow down)</li>
<li><strong>Transparency with depth-based color</strong> (shallow is clear, deep is dark)</li>
</ul>
<p>For a browser world, river data is compact: a spline (20-50 control points per river segment, ~400 bytes) plus width and flow speed parameters. The client generates the river mesh locally by projecting the spline onto its terrain surface.</p>
<h3 id="waterfall-rendering" tabindex="-1">Waterfall Rendering <a class="header-anchor" href="#waterfall-rendering" aria-label="Permalink to &quot;Waterfall Rendering&quot;"></a></h3>
<p>Where a river drops off a cliff face, a waterfall particle system replaces the flat river surface. The hybrid approach from real-time water simulation research (<a href="https://diglib.eg.org/items/d0320015-4b07-416b-8f41-047485c9f7f3" target="_blank" rel="noreferrer">EG</a>): regions that can't be represented by a height field (waterfalls, splashes) convert to spray, splash, and foam particles that exchange mass and momentum with the fluid simulation.</p>
<p>For a browser world, waterfalls are simpler: detect where the river spline crosses a terrain height discontinuity, spawn a particle system at that point with downward velocity, and add a foam splash at the base. The particles are GPU-instanced quads with scrolling alpha textures. 500 particles per waterfall, one draw call. The sound of falling water uses the Web Audio API with distance attenuation.</p>
<h2 id="impostors-for-distant-terrain-features" tabindex="-1">Impostors for Distant Terrain Features <a class="header-anchor" href="#impostors-for-distant-terrain-features" aria-label="Permalink to &quot;Impostors for Distant Terrain Features&quot;"></a></h2>
<p>Trees, rocks, buildings, and other terrain features become tiny at distance. Rendering them as full 3D meshes wastes GPU cycles. Impostors replace distant objects with pre-rendered flat images that face the camera.</p>
<h3 id="octahedral-impostor-atlases" tabindex="-1">Octahedral Impostor Atlases <a class="header-anchor" href="#octahedral-impostor-atlases" aria-label="Permalink to &quot;Octahedral Impostor Atlases&quot;"></a></h3>
<p>An octahedral impostor captures the appearance of a 3D object from multiple viewing angles and stores them in a texture atlas. At runtime, the shader samples the atlas based on the current view direction, interpolating between the two closest captured angles.</p>
<p>A hemi-octahedral atlas (views from the upper hemisphere only, since you rarely look at trees from below) provides twice the angular resolution of a full octahedral atlas with the same texture size. Unity's impostor system reports frame time dropping from 111ms to 5.78ms when switching 1,600 tree instances from real meshes (140K triangles each) to impostors.</p>
<p>For a browser world, the impostor pipeline:</p>
<ol>
<li>Server-side: render each asset from 16-32 viewing angles, capturing color, normal, and depth</li>
<li>Pack into an atlas texture (one atlas per asset, ~256x256 pixels, &lt;100 KB as KTX2)</li>
<li>At runtime: instances beyond the impostor distance (typically 100-200m) render as billboards sampling the atlas</li>
<li>Crossfade between mesh and impostor over a 20m transition zone to hide the switch</li>
</ol>
<h3 id="billboard-splatting-bbsplat" tabindex="-1">Billboard Splatting (BBSplat) <a class="header-anchor" href="#billboard-splatting-bbsplat" aria-label="Permalink to &quot;Billboard Splatting (BBSplat)&quot;"></a></h3>
<p>Billboard Splatting (2024, <a href="https://arxiv.org/abs/2411.08508" target="_blank" rel="noreferrer">arXiv:2411.08508</a>) takes this further by using learnable textured planar primitives. Instead of pre-rendered views, BBSplat optimizes billboard positions and textures to best represent the 3D object from any angle. This achieves up to 17x compression compared to 3D Gaussian Splatting while maintaining view-dependent appearance. For distant terrain features in a browser world, BBSplat could reduce the per-asset impostor storage while improving angular coverage.</p>
<h2 id="professional-terrain-tools-and-what-they-teach" tabindex="-1">Professional Terrain Tools and What They Teach <a class="header-anchor" href="#professional-terrain-tools-and-what-they-teach" aria-label="Permalink to &quot;Professional Terrain Tools and What They Teach&quot;"></a></h2>
<p>Before building a terrain pipeline from scratch, it's worth understanding what professional offline tools do. These tools represent decades of terrain generation research distilled into production workflows.</p>
<p><strong>Gaea</strong> (QuadSpinner) is GPU-accelerated and provides near-instant feedback on changes. It supports tiled builds up to 2 million pixels per side, automatic LOD mesh export, and a node-based graph where each node is a physical process (erosion, sedimentation, uplift, thermal weathering). Gaea's erosion nodes produce terrain that looks hand-sculpted because they model specific physical processes rather than generic noise. The key insight: Gaea doesn't use a single erosion algorithm. It offers separate nodes for fluvial erosion (river carving), thermal erosion (cliff crumbling), coastal erosion (wave action), and wind erosion (sand dune formation). Combining them in a graph produces terrain with the geological character of a specific climate.</p>
<p><strong>World Machine</strong> takes a similar graph approach with a focus on terrain macro-structure. Its &quot;layout generator&quot; lets artists sketch the rough shape of terrain features (mountain here, valley there, coastline along this edge) and the system fills in physically plausible detail. This is exactly the creator workflow we want: sketch intent, get geology.</p>
<p><strong>World Creator</strong> differentiates itself with real-time preview during editing and built-in river generation that analyzes the terrain and auto-calculates flow paths based on drainage analysis.</p>
<p><strong>What we take from these tools:</strong> The node-graph approach to combining physical processes is more powerful than any single algorithm. Our server-side generation pipeline should support chaining: noise base &gt; tectonic uplift &gt; hydraulic erosion &gt; thermal weathering &gt; coastal erosion &gt; vegetation. Creators control parameters at each stage. The pipeline runs server-side in seconds and produces heightmaps, splat maps, and vegetation density maps.</p>
<h3 id="soilmachine-open-source-geomorphology" tabindex="-1">SoilMachine: Open-Source Geomorphology <a class="header-anchor" href="#soilmachine-open-source-geomorphology" aria-label="Permalink to &quot;SoilMachine: Open-Source Geomorphology&quot;"></a></h3>
<div class="img-embed">
<img src="https://cdn.cinevva.com/blog/soilmachine-banner.png" alt="SoilMachine: open-source modular geomorphology simulator showing terrain with coupled hydraulic, thermal, and wind erosion" />
</div>
<div class="game-caption">SoilMachine couples hydraulic, thermal, and wind erosion in a single layered terrain model. The multi-layer data structure supports features that heightmaps can't represent: caves, overhangs, buttes, and groundwater distributions.</div>
<p><a href="https://github.com/weigert/soilmachine" target="_blank" rel="noreferrer">SoilMachine</a> is an open-source modular geomorphology simulator that couples multiple erosion systems (hydraulic, thermal, wind) with sediment transport and deposition. Built in C++ with GPU compute, it provides a reference implementation of the multi-process erosion approach that professional tools use.</p>
<p>The related <a href="https://github.com/erosiv/soillib" target="_blank" rel="noreferrer">soillib</a> (C++20, MIT license) provides the underlying geomorphology simulation primitives as a reusable library. And <a href="https://github.com/ger0/hydro-gen" target="_blank" rel="noreferrer">hydro-gen</a> implements both grid-based (shallow water) and particle-based (raindrop) hydraulic erosion in OpenGL compute shaders with real-time parameter adjustment.</p>
<p>These open-source tools could be adapted for our server-side generation pipeline. The compute shader implementations translate directly to WebGPU if we ever want to run erosion in the browser for real-time creator feedback.</p>
<h2 id="multi-layer-terrain-materials" tabindex="-1">Multi-Layer Terrain Materials <a class="header-anchor" href="#multi-layer-terrain-materials" aria-label="Permalink to &quot;Multi-Layer Terrain Materials&quot;"></a></h2>
<p>Real terrain isn't a single surface. It's layers: bedrock at the bottom, soil on top, snow or sand accumulating on surfaces. Dynamic layering changes the visual character of terrain with seasons, weather, and creator actions.</p>
<h3 id="layered-heightfield-representation" tabindex="-1">Layered Heightfield Representation <a class="header-anchor" href="#layered-heightfield-representation" aria-label="Permalink to &quot;Layered Heightfield Representation&quot;"></a></h3>
<p>Instead of a single heightmap, use multiple height layers per grid cell:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Cell {</span></span>
<span class="line"><span>  bedrock_height: f16,    // permanent rock surface</span></span>
<span class="line"><span>  soil_height: f16,       // accumulated soil/sediment above bedrock</span></span>
<span class="line"><span>  snow_height: f16,       // dynamic snow accumulation</span></span>
<span class="line"><span>  water_height: f16       // standing water depth</span></span>
<span class="line"><span>}</span></span></code></pre>
</div><p>Total: 8 bytes per cell (vs 2 bytes for a single heightmap). For a 65x65 chunk, that's 34 KB before compression. Still compact.</p>
<p>The visual surface is <code>bedrock + soil + snow</code>. The terrain shader reads all layers and blends materials accordingly: where soil is thin, rock shows through. Where snow has accumulated, the surface is white. Where water sits, you get puddles or lakes.</p>
<h3 id="dynamic-accumulation" tabindex="-1">Dynamic Accumulation <a class="header-anchor" href="#dynamic-accumulation" aria-label="Permalink to &quot;Dynamic Accumulation&quot;"></a></h3>
<p>Snow accumulates on flat, upward-facing surfaces during snowfall. The accumulation rate depends on surface normal (steep slopes don't hold snow), temperature (altitude-dependent), and shelter (areas below overhangs stay clear). A compute shader pass updates the snow layer once per weather tick (every few seconds).</p>
<p>Sand accumulation works similarly with wind-driven deposition. Wind carries particles from exposed surfaces and deposits them behind obstacles and in sheltered areas.</p>
<p>For a creator world, dynamic accumulation means the terrain looks different in different weather. Snow covers the world during a blizzard and melts during a clear spell. Rain fills depressions with water. This makes the world feel responsive without creators doing anything.</p>
<h3 id="multi-layered-erosion" tabindex="-1">Multi-Layered Erosion <a class="header-anchor" href="#multi-layered-erosion" aria-label="Permalink to &quot;Multi-Layered Erosion&quot;"></a></h3>
<p>The 2024 paper &quot;3D Real-Time Hydraulic Erosion Simulation using Multi-Layered Heightmaps&quot; (<a href="https://diglib.eg.org/items/deefa865-6a25-4463-adcd-d8de2b37507e" target="_blank" rel="noreferrer">EG</a>) extends erosion to work across layers. Water erodes soil faster than bedrock. Sediment deposits as a new soil layer. The simulation maintains layer integrity (bedrock stays below soil) while enabling complex features like overhangs (where bedrock overhangs eroded soil below).</p>
<p>Performance: approximately 6ms per simulation step on an RTX 3070 at 2048x2048 resolution. This is fast enough for server-side generation but too slow for per-frame browser simulation. The layered representation works for static terrain generation, and the dynamic snow/water accumulation runs as a cheaper per-frame shader.</p>
<h2 id="grass-rock-and-detail-rendering" tabindex="-1">Grass, Rock, and Detail Rendering <a class="header-anchor" href="#grass-rock-and-detail-rendering" aria-label="Permalink to &quot;Grass, Rock, and Detail Rendering&quot;"></a></h2>
<p>The landscape needs more than terrain geometry and textures. It needs grass blades that sway in the wind, rocks scattered on slopes, and small details like flowers, pebbles, and fallen branches that make close-up views look natural.</p>
<h3 id="gpu-instanced-grass" tabindex="-1">GPU-Instanced Grass <a class="header-anchor" href="#gpu-instanced-grass" aria-label="Permalink to &quot;GPU-Instanced Grass&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/pzkcM1rfKZw" title="Rendering 1 Million Blades Of Grass In Unity" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">One million grass blades rendered with GPU instancing in a single draw call. The same technique works in Three.js and WebGPU: per-instance transforms, vertex-shader wind animation, and a density map to scatter blades from terrain data.</div>
<p>Browser-based grass rendering is well-proven in Three.js and works through GPU instancing. The approach from <a href="https://al-ro.github.io/projects/grass/" target="_blank" rel="noreferrer">al-ro's grass demo</a> renders 100,000 grass blades with a single draw call using <code>InstancedBufferGeometry</code>.</p>
<p>Each grass blade is a simple quad (4-8 triangles). Per-instance attributes define position, height, bend direction, color variation, and wind phase. The vertex shader:</p>
<ol>
<li>Reads the per-instance transform</li>
<li>Applies wind animation using sine waves keyed to world position and time</li>
<li>Bends the blade based on wind strength (more bend at the tip, none at the base)</li>
<li>Applies color gradient (darker at the base, lighter at the tip for subsurface scattering)</li>
</ol>
<p><strong>Codrops' fluffy grass tutorial</strong> (2025, <a href="https://tympanus.net/codrops/2025/02/04/how-to-make-the-fluffiest-grass-with-three-js/" target="_blank" rel="noreferrer">Tutorial</a>) demonstrates a shell texturing approach: render the ground plane multiple times at increasing offsets, with each layer sampling a noise texture to create the appearance of dense grass volume. This is cheaper than individual blade instancing for very dense coverage but less realistic at close range.</p>
<p>For a creator world, grass density comes from the vegetation density map per chunk. The GPU scatters blade positions from the density map at render time. No per-blade data is stored or streamed. The density map is a 32x32 grid per chunk (1 KB), and the GPU generates thousands of blade instances from it.</p>
<h3 id="procedural-rock-and-cliff-detail" tabindex="-1">Procedural Rock and Cliff Detail <a class="header-anchor" href="#procedural-rock-and-cliff-detail" aria-label="Permalink to &quot;Procedural Rock and Cliff Detail&quot;"></a></h3>
<p>Cliff faces and rocky terrain need geometric detail that the base heightmap or SDF can't provide at reasonable resolution. Two approaches complement each other:</p>
<p><strong>GPU mesh shader resurfacing</strong> (Raad et al., Eurographics 2025, <a href="https://hal.science/hal-05176868v1/file/Paper_Resurfacing_2024.pdf" target="_blank" rel="noreferrer">Paper</a>) generates procedural geometry at render time from a coarse control mesh. The mesh shader reads a base terrain surface and adds displacement, cracks, and protrusions without storing the detail geometry in memory. This reduces VRAM usage and enables dynamic LOD.</p>
<p><strong>Instanced rock scattering</strong> places pre-made rock meshes on steep slopes and cliff edges using GPU instancing. A compute shader reads the terrain normal and slope, and scatters rock instances where the slope exceeds a threshold. Each instance is a small mesh (200-500 triangles) with random rotation and scale. 10,000 scattered rocks add negligible rendering cost with instancing.</p>
<h3 id="roads-and-paths" tabindex="-1">Roads and Paths <a class="header-anchor" href="#roads-and-paths" aria-label="Permalink to &quot;Roads and Paths&quot;"></a></h3>
<p>Creator-placed roads, trails, and paths need to conform to terrain and modify the surface material (replacing grass with dirt or stone).</p>
<p>The approach used by every major game engine: define the path as a spline (a series of control points). Project the spline onto the terrain surface. Generate a strip mesh that follows the spline and sits slightly above the terrain. Apply a road texture to the strip. In the terrain shader, blend the terrain material toward the road material within the spline's width using a projected texture or decal.</p>
<p>For a browser world, a creator draws a path on the terrain. The client generates control points and sends them to the server (a few dozen vec3 values). The server stores the spline. All clients render the road strip locally by projecting the spline onto their terrain mesh. The road data is tiny (path spline points, maybe 200 bytes) but the visual impact is large: paths connecting creator builds make the world feel inhabited.</p>
<h2 id="shadows-for-terrain" tabindex="-1">Shadows for Terrain <a class="header-anchor" href="#shadows-for-terrain" aria-label="Permalink to &quot;Shadows for Terrain&quot;"></a></h2>
<p>Terrain shadows are critical for readability (understanding terrain shape) and atmosphere (time-of-day mood). In an open world, the sun casts shadows across the entire visible terrain.</p>
<h3 id="cascaded-shadow-maps-csm" tabindex="-1">Cascaded Shadow Maps (CSM) <a class="header-anchor" href="#cascaded-shadow-maps-csm" aria-label="Permalink to &quot;Cascaded Shadow Maps (CSM)&quot;"></a></h3>
<p>CSM splits the view frustum into 3-4 distance ranges (cascades). Each cascade renders a shadow map from the sun's perspective at a resolution appropriate for its distance. Close cascade: high resolution (detailed shadows under trees and buildings). Far cascade: low resolution (broad mountain shadows).</p>
<p>Both Three.js and Babylon.js support CSM. The key optimization for terrain: only render terrain in the shadow map, not individual grass blades or small details. Grass self-shadows using the terrain's shadow map, not its own.</p>
<p><strong>Performance budget:</strong> 3-4 shadow cascades at 1024x1024 each. Rendering terrain into shadow maps costs 0.5-1ms (the terrain geometry is already in GPU memory). Sampling 4 cascades in the terrain shader adds 0.2-0.3ms.</p>
<h3 id="terrain-self-shadowing-from-heightmap" tabindex="-1">Terrain Self-Shadowing from Heightmap <a class="header-anchor" href="#terrain-self-shadowing-from-heightmap" aria-label="Permalink to &quot;Terrain Self-Shadowing from Heightmap&quot;"></a></h3>
<p>For very large terrain where CSM becomes expensive, pre-compute a horizon map: for each terrain cell, store the maximum elevation angle in 8 compass directions. At render time, compare the sun angle to the horizon map to determine if a point is in shadow. This is how Skyrim handles terrain self-shadows for distant terrain (beyond CSM range).</p>
<p>The horizon map is computed server-side from the heightmap (a few seconds of processing) and streams as a 128x128 per-chunk texture (16 KB compressed). The visual impact is significant: mountain valleys darken realistically even at extreme view distances.</p>
<h2 id="terrain-data-compression-for-streaming" tabindex="-1">Terrain Data Compression for Streaming <a class="header-anchor" href="#terrain-data-compression-for-streaming" aria-label="Permalink to &quot;Terrain Data Compression for Streaming&quot;"></a></h2>
<p>The network is the bottleneck for a browser world. Every byte saved in terrain data is a faster load time.</p>
<h3 id="heightmap-compression" tabindex="-1">Heightmap Compression <a class="header-anchor" href="#heightmap-compression" aria-label="Permalink to &quot;Heightmap Compression&quot;"></a></h3>
<p>Raw 16-bit heightmaps compress well because adjacent cells have similar values. The pipeline:</p>
<ol>
<li><strong>Delta encoding:</strong> Store the difference between each cell and its predicted value (average of neighbors). Delta values are small, clustering near zero.</li>
<li><strong>Quantization:</strong> For distant chunks, reduce precision from 16-bit to 12-bit or 8-bit. At 500 meters away, 8-bit height precision (0.4m resolution on a 100m height range) is indistinguishable from 16-bit.</li>
<li><strong>Entropy coding:</strong> Apply zlib or brotli compression to the delta-encoded stream. Typical compression ratio: 4-8x.</li>
</ol>
<p>Result: a 65x65 chunk at 16-bit goes from 8.4 KB raw to 1-2 KB compressed. At 8-bit reduced precision: 0.5-1 KB.</p>
<h3 id="progressive-heightmap-streaming" tabindex="-1">Progressive Heightmap Streaming <a class="header-anchor" href="#progressive-heightmap-streaming" aria-label="Permalink to &quot;Progressive Heightmap Streaming&quot;"></a></h3>
<p>Send terrain at low resolution first, then refine. A 17x17 heightmap (the minimum for a 64m chunk with 4m cell spacing) is 578 bytes raw, under 200 bytes compressed. The terrain is visible instantly. Then stream the 33x33 refinement (adds odd-row/column samples). Then the 65x65 full resolution. Each level adds detail without replacing previous data.</p>
<p>This maps to the geometry clipmap LOD rings: distant terrain uses the low-resolution version (17x17), mid-range uses medium (33x33), close-up uses full (65x65). The streaming priority matches the rendering LOD.</p>
<h3 id="sdf-volume-compression" tabindex="-1">SDF Volume Compression <a class="header-anchor" href="#sdf-volume-compression" aria-label="Permalink to &quot;SDF Volume Compression&quot;"></a></h3>
<p>Sparse SDF volumes compress dramatically because most voxels are far from the surface (empty space). Options:</p>
<p><strong>Run-length encoding:</strong> Encode runs of identical values (empty voxels). Typical SDF volumes are 95%+ empty, so RLE achieves 10-50x compression.</p>
<p><strong>Sparse octree:</strong> Only store octree nodes that contain surface-crossing voxels. Empty space has no nodes. A 64^3 SDF volume with a single cave tunnel might have only 2,000-5,000 occupied nodes (vs 262,144 total voxels), each stored as 1-2 bytes.</p>
<p><strong>Entropy-driven progressive compression</strong> (2024, <a href="https://inria.hal.science/hal-04630545v1/file/sgp2024.pdf" target="_blank" rel="noreferrer">HAL</a>) applies to 3D spatial data by recursively partitioning space with entropy-optimized planes and adaptive quantization. This produces a stream of refinements optimized for rate-distortion trade-offs, particularly beneficial at the low bitrates of network streaming.</p>
<h2 id="putting-it-all-together-the-browser-terrain-pipeline" tabindex="-1">Putting It All Together: The Browser Terrain Pipeline <a class="header-anchor" href="#putting-it-all-together-the-browser-terrain-pipeline" aria-label="Permalink to &quot;Putting It All Together: The Browser Terrain Pipeline&quot;"></a></h2>
<p>The strategic overview at the top of this article gives the quick decision framework and phased build plan. This section provides the full technical detail for both the server-side generation pipeline and the browser rendering pipeline.</p>
<h3 id="generation-pipeline-server-side" tabindex="-1">Generation Pipeline (Server-Side) <a class="header-anchor" href="#generation-pipeline-server-side" aria-label="Permalink to &quot;Generation Pipeline (Server-Side)&quot;"></a></h3>
<p>The generation pipeline runs as a directed graph of physical processes, inspired by Gaea and World Machine's node-graph approach. Each stage takes the previous stage's output and refines it. Creators control parameters at each stage.</p>
<table tabindex="0">
<thead>
<tr>
<th>Stage</th>
<th>Input</th>
<th>Process</th>
<th>Output</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. Base terrain</td>
<td>Seed or text prompt</td>
<td>Terrain Diffusion / MESA / noise + fBm</td>
<td>16-bit heightmap</td>
<td>1-5s</td>
</tr>
<tr>
<td>2. Erosion</td>
<td>Heightmap</td>
<td>Analytical stream power + thermal erosion</td>
<td>Eroded heightmap, flow accumulation map, sediment map</td>
<td>0.5-2s</td>
</tr>
<tr>
<td>3. Rivers</td>
<td>Eroded heightmap, flow map</td>
<td>Drainage network extraction, channel carving</td>
<td>River splines, water level map</td>
<td>0.5s</td>
</tr>
<tr>
<td>4. Coast</td>
<td>Heightmap near sea level</td>
<td>NEWTS-style wave erosion</td>
<td>Coastline features (cliffs, beaches, stacks)</td>
<td>1-3s</td>
</tr>
<tr>
<td>5. Volumetric</td>
<td>Heightmap + creator intent</td>
<td>Arenite erosion / cave generation / SDF sculpts</td>
<td>Sparse SDF volumes for affected chunks</td>
<td>1-60s</td>
</tr>
<tr>
<td>6. Materials</td>
<td>Heightmap + erosion maps</td>
<td>TerraFusion / Geodiffussr / procedural rules</td>
<td>Splat maps, terrain textures</td>
<td>1-5s</td>
</tr>
<tr>
<td>7. Vegetation</td>
<td>Heightmap + flow map + materials</td>
<td>Ecosystem competition simulation</td>
<td>Density maps per biome per chunk</td>
<td>1-3s</td>
</tr>
<tr>
<td>8. Horizon maps</td>
<td>Final heightmap</td>
<td>8-direction max elevation angle</td>
<td>Self-shadow texture per chunk</td>
<td>2-5s</td>
</tr>
<tr>
<td>9. Chunking</td>
<td>All outputs</td>
<td>Slice, delta-encode, compress, hash</td>
<td>Chunk packages on CDN</td>
<td>5-10s</td>
</tr>
<tr>
<td><strong>Total</strong></td>
<td></td>
<td></td>
<td></td>
<td><strong>15-90s</strong></td>
</tr>
</tbody>
</table>
<p>A new 4x4 km world generates in 15-90 seconds. Creator edits (sculpting, parameter changes) re-run only the affected stages for the affected chunks, typically completing in under 5 seconds.</p>
<h3 id="rendering-pipeline-browser" tabindex="-1">Rendering Pipeline (Browser) <a class="header-anchor" href="#rendering-pipeline-browser" aria-label="Permalink to &quot;Rendering Pipeline (Browser)&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Step</th>
<th>WebGPU path</th>
<th>WebGL 2 fallback</th>
<th>Frame budget</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. Streaming</td>
<td>Priority queue, predictive pre-fetch</td>
<td>Same</td>
<td>N/A (async)</td>
</tr>
<tr>
<td>2. Heightmap terrain</td>
<td>GPU-driven CDLOD quadtree, compute culling, indirect draw</td>
<td>Geometry clipmaps, CPU ring updates</td>
<td>0.5-1ms</td>
</tr>
<tr>
<td>3. Volumetric mesh</td>
<td>Compute marching cubes + Transvoxel</td>
<td>Pre-generated meshes from Web Worker, 2-3 cached LODs</td>
<td>0.5-2ms</td>
</tr>
<tr>
<td>4. LOD transitions</td>
<td>Geomorphing in vertex shader</td>
<td>Same</td>
<td>Included above</td>
</tr>
<tr>
<td>5. Materials</td>
<td>Triplanar PBR + Laplacian blending + phasor noise detail + virtual texturing</td>
<td>Triplanar PBR + linear blending + pre-baked splat maps</td>
<td>1-1.5ms</td>
</tr>
<tr>
<td>6. Vegetation</td>
<td>ComputeInstanceCulling + IndirectBatchedMesh, hex-tiled ground cover</td>
<td>CPU frustum cull + InstancedMesh</td>
<td>1-1.5ms</td>
</tr>
<tr>
<td>7. Water</td>
<td>Flow-aligned river strips, depth-based shoreline foam</td>
<td>Same (simpler reflections)</td>
<td>0.5ms</td>
</tr>
<tr>
<td>8. Shadows</td>
<td>3-4 cascade CSM + horizon map self-shadows</td>
<td>2 cascade CSM</td>
<td>0.5-1ms</td>
</tr>
<tr>
<td>9. Atmosphere</td>
<td>Hillaire sky model + volumetric fog + weather particles</td>
<td>Preetham sky + distance fog</td>
<td>0.5ms</td>
</tr>
<tr>
<td>10. Dynamic effects</td>
<td>Puddle accumulation, footprint deformation, snow/rain</td>
<td>Puddle accumulation, rain particles</td>
<td>0.3ms</td>
</tr>
<tr>
<td><strong>Total terrain</strong></td>
<td></td>
<td></td>
<td><strong>3.5-6.5ms</strong></td>
</tr>
</tbody>
</table>
<p>At 60fps (16.6ms per frame), the terrain system uses 21% of the frame budget on WebGPU and 39% on WebGL 2. The remainder is available for player avatars, creator objects, UI, networking, and post-processing.</p>
<h3 id="why-this-works-in-a-browser" tabindex="-1">Why This Works in a Browser <a class="header-anchor" href="#why-this-works-in-a-browser" aria-label="Permalink to &quot;Why This Works in a Browser&quot;"></a></h3>
<p>The entire pipeline is designed around three browser constraints:</p>
<p><strong>Memory (2-4 GB max):</strong> The 256 MB terrain budget fits because heightmap chunks are 2-8 KB each (delta-encoded), SDF volumes are sparse (100-500 KB per volumetric chunk), vegetation is generated at runtime from 1 KB density maps, and textures use KTX2 compression (150 KB per 1024x1024). At any moment, the world visible in the browser weighs 50-200 MB total.</p>
<p><strong>No disk access:</strong> Everything streams over the network. Progressive loading means the player sees terrain in &lt;100ms (low-res heightmap), textured terrain in &lt;300ms, and full detail in &lt;3s. Pre-fetching based on velocity hides load times during normal exploration.</p>
<p><strong>GPU varies wildly:</strong> The WebGPU path handles high-end desktops. The WebGL 2 fallback handles everything else, including mobile. The same chunk data drives both paths. The difference is rendering technique, not data format. A Chromebook running WebGL 2 sees the same world as an RTX 4090 running WebGPU, just at lower detail and shorter view distance.</p>
<h2 id="research-papers" tabindex="-1">Research Papers <a class="header-anchor" href="#research-papers" aria-label="Permalink to &quot;Research Papers&quot;"></a></h2>
<h3 id="terrain-representation-and-mesh-generation" tabindex="-1">Terrain Representation and Mesh Generation <a class="header-anchor" href="#terrain-representation-and-mesh-generation" aria-label="Permalink to &quot;Terrain Representation and Mesh Generation&quot;"></a></h3>
<p><strong>&quot;Marching Cubes: A High Resolution 3D Surface Construction Algorithm&quot;</strong> -- Lorensen and Cline (SIGGRAPH 1987). <a href="https://doi.org/10.1145/37402.37422" target="_blank" rel="noreferrer">DOI</a>. The foundational algorithm for extracting triangle meshes from volumetric data. Still the most widely used isosurface extraction method 38 years later. GPU-parallel implementations run in real time in WebGPU compute shaders.</p>
<p><strong>&quot;Dual Contouring of Hermite Data&quot;</strong> -- Ju, Losasso, Schaefer, Warren (SIGGRAPH 2002). <a href="https://doi.org/10.1145/566570.566586" target="_blank" rel="noreferrer">DOI</a>. Produces meshes that preserve sharp features (cliff edges, rock corners) that marching cubes rounds off. Requires surface normals in addition to distance values.</p>
<p><strong>&quot;Neural Dual Contouring&quot;</strong> -- Chen et al. (2022). <a href="https://arxiv.org/abs/2202.01999" target="_blank" rel="noreferrer">arXiv:2202.01999</a>. Replaces the least-squares vertex placement in dual contouring with a learned predictor. Better surface quality for complex natural features.</p>
<p><strong>&quot;The Transvoxel Algorithm&quot;</strong> -- Lengyel (2009, updated 2024). <a href="https://transvoxel.org/" target="_blank" rel="noreferrer">transvoxel.org</a>. Seamless LOD transitions for voxel terrain. Eliminates cracks at resolution boundaries using 73 transition cell types. Patent-free, designed for real-time applications.</p>
<h3 id="terrain-lod-and-rendering" tabindex="-1">Terrain LOD and Rendering <a class="header-anchor" href="#terrain-lod-and-rendering" aria-label="Permalink to &quot;Terrain LOD and Rendering&quot;"></a></h3>
<p><strong>&quot;Geometry Clipmaps: Terrain Rendering Using Nested Regular Grids&quot;</strong> -- Losasso and Hoppe (SIGGRAPH 2004). <a href="https://hhoppe.com/geomclipmap.pdf" target="_blank" rel="noreferrer">Paper</a>. Constant-cost terrain rendering with concentric LOD rings. Handles 40 GB terrains at interactive rates. The foundation for most browser terrain renderers.</p>
<p><strong>&quot;CDLOD: Hybrid LOD for Terrain Rendering&quot;</strong> -- Strugar (2014). <a href="https://www.vertexasylum.com/CDLOD/cdlod_latest.pdf" target="_blank" rel="noreferrer">Paper</a>. Quadtree-adaptive improvement on geometry clipmaps. Allocates resolution based on terrain complexity rather than distance alone.</p>
<p><strong>&quot;GPU-Driven Rendering Pipelines&quot;</strong> -- Ubisoft (SIGGRAPH 2015), Wihlidal and Hoppe. Formalized the GPU-driven approach where compute shaders handle culling, LOD selection, and draw call generation. The architectural pattern for our WebGPU terrain pipeline.</p>
<h3 id="physical-terrain-generation" tabindex="-1">Physical Terrain Generation <a class="header-anchor" href="#physical-terrain-generation" aria-label="Permalink to &quot;Physical Terrain Generation&quot;"></a></h3>
<p><strong>&quot;Physically-Based Analytical Erosion for Fast Terrain Generation&quot;</strong> -- Cordonnier et al. (2024). <a href="https://hal.science/hal-04525371" target="_blank" rel="noreferrer">HAL</a>. Analytical stream power law erosion that avoids iterative simulation. Generates physically plausible terrain in milliseconds.</p>
<p><strong>&quot;Fast Hydraulic Erosion Simulation and Visualization on GPU&quot;</strong> -- Mei, Decaudin, Hu (2007). <a href="https://hal.science/hal-02281637/file/main.pdf" target="_blank" rel="noreferrer">HAL</a>. GPU-parallel hydraulic erosion using shallow-water simulation. The basis for most game engine erosion implementations.</p>
<p><strong>&quot;Arenite: A Physics-Based Sandstone Simulator&quot;</strong> -- SIGGRAPH 2025. <a href="https://jango6324.github.io/arenite/" target="_blank" rel="noreferrer">Project</a>. Multi-physics erosion generating arches, hoodoos, and alcoves from stress and erosion simulation. Runs in under 5 minutes on desktop GPUs.</p>
<p><strong>&quot;Efficient Debris-flow Simulation for Steep Terrain Erosion&quot;</strong> -- Purdue CGVLAB (2024). <a href="https://www.cs.purdue.edu/cgvlab/www/resources/papers/Arymaan-ToG-2024-efficient.pdf" target="_blank" rel="noreferrer">Paper</a>. GPU-accelerated debris flow and steep-slope erosion producing realistic mountain terrain features.</p>
<p><strong>&quot;Flexible Terrain Erosion&quot;</strong> -- IRIT-STORM (2024). <a href="https://link.springer.com/article/10.1007/s00371-024-03444-w" target="_blank" rel="noreferrer">Springer</a>. Particle-based erosion that works across heightfields, voxel grids, implicit surfaces, and layered materials with a unified interface. Enables one erosion system for hybrid terrain representations.</p>
<h3 id="terrain-detail-and-texturing" tabindex="-1">Terrain Detail and Texturing <a class="header-anchor" href="#terrain-detail-and-texturing" aria-label="Permalink to &quot;Terrain Detail and Texturing&quot;"></a></h3>
<p><strong>&quot;GPU-Friendly Laplacian Texture Blending&quot;</strong> -- Wronski (NVIDIA, 2025). <a href="https://jcgt.org/published/0014/01/02/" target="_blank" rel="noreferrer">JCGT</a>. Real-time Laplacian pyramid blending for terrain materials without precomputation. Preserves sharp features while eliminating seam artifacts. A few extra texture taps per fragment.</p>
<p><strong>&quot;Real-time Terrain Enhancement with Controlled Procedural Patterns&quot;</strong> -- Grenier et al. (2024). <a href="https://diglib.eg.org/bitstream/handle/10.1111/cgf14992/v43i1_05_cgf14992.pdf" target="_blank" rel="noreferrer">CGF</a>. Phasor noise-based micro-erosion detail at up to 32x heightmap resolution. Slope-aligned patterns run entirely in a fragment shader.</p>
<h3 id="adaptive-tessellation" tabindex="-1">Adaptive Tessellation <a class="header-anchor" href="#adaptive-tessellation" aria-label="Permalink to &quot;Adaptive Tessellation&quot;"></a></h3>
<p><strong>&quot;Concurrent Binary Trees for Large-Scale Game Components&quot;</strong> -- Benyoub and Dupuy (Intel, HPG 2024). <a href="https://arxiv.org/html/2407.02215v1" target="_blank" rel="noreferrer">Paper</a>. GPU-friendly binary tree data structure for adaptive terrain tessellation. Renders planetary-scale geometry in under 0.2ms. Extended from square domains to arbitrary polygon meshes.</p>
<h3 id="cave-and-underground-generation-1" tabindex="-1">Cave and Underground Generation <a class="header-anchor" href="#cave-and-underground-generation-1" aria-label="Permalink to &quot;Cave and Underground Generation&quot;"></a></h3>
<p><strong>&quot;PLUME: Procedural Layer Underground Modeling Engine&quot;</strong> -- 2024. <a href="https://arxiv.org/html/2508.20926v1" target="_blank" rel="noreferrer">arXiv:2508.20926</a>. Open-source framework for generating realistic cave and lava tube environments using layered procedural rules. Originally built for space exploration robotics.</p>
<h3 id="neural-terrain-synthesis" tabindex="-1">Neural Terrain Synthesis <a class="header-anchor" href="#neural-terrain-synthesis" aria-label="Permalink to &quot;Neural Terrain Synthesis&quot;"></a></h3>
<p><strong>&quot;InfiniteDiffusion: Bridging Learned Fidelity and Procedural Utility for Open-World Terrain Generation&quot;</strong> -- Goslin (2025, SIGGRAPH 2026). <a href="https://arxiv.org/abs/2512.08309" target="_blank" rel="noreferrer">arXiv:2512.08309</a>. Infinite, seed-consistent terrain generation using hierarchical diffusion models with Laplacian encoding. 9x faster than baseline on consumer GPUs.</p>
<p><strong>&quot;TerraFusion: Joint Generation of Terrain Geometry and Texture&quot;</strong> -- 2025. <a href="https://arxiv.org/abs/2505.04050" target="_blank" rel="noreferrer">arXiv:2505.04050</a>. Latent diffusion for simultaneous heightmap and texture synthesis with sketch conditioning.</p>
<p><strong>&quot;MESA: Text-Driven Terrain Generation&quot;</strong> -- CVPR 2025 Workshop. <a href="https://arxiv.org/abs/2504.07210" target="_blank" rel="noreferrer">arXiv:2504.07210</a>. Text-to-terrain using Copernicus remote sensing training data.</p>
<p><strong>&quot;Geodiffussr: Generative Terrain Texturing with Elevation Fidelity&quot;</strong> -- 2025. <a href="https://arxiv.org/abs/2511.23029" target="_blank" rel="noreferrer">arXiv:2511.23029</a>. Text-guided terrain texture generation that respects elevation data using flow-matching.</p>
<p><strong>&quot;Sketch2Terrain: AI-Driven Real-Time Terrain Sketch Mapping&quot;</strong> -- 2025. <a href="https://tianyixiao.top/publication/2025-sketch2terrain" target="_blank" rel="noreferrer">Project</a>. Sketch-to-terrain in augmented reality. 38% efficiency improvement over manual mapping.</p>
<h3 id="vegetation-and-ecosystem-simulation" tabindex="-1">Vegetation and Ecosystem Simulation <a class="header-anchor" href="#vegetation-and-ecosystem-simulation" aria-label="Permalink to &quot;Vegetation and Ecosystem Simulation&quot;"></a></h3>
<p><strong>&quot;GPU-Based Real-Time Procedural Distribution of Vegetation on Large-Scale Virtual Terrains&quot;</strong> -- SBGames 2018. <a href="https://sbgames.org/sbgames2018/files/papers/ComputacaoFull/188348.pdf" target="_blank" rel="noreferrer">Paper</a>. Quadtree-based vegetation scattering using biotic/abiotic factors on GPU.</p>
<p><strong>&quot;Procedural Generation and Rendering of Forests&quot;</strong> -- 2022. <a href="https://export.arxiv.org/pdf/2208.01471v1.pdf" target="_blank" rel="noreferrer">Paper</a>. L-system tree generation combined with ecosystem competition simulation for realistic forest distribution.</p>
<p><strong>&quot;Real-Time Procedural Generation with GPU Work Graphs&quot;</strong> -- AMD GPUOpen 2024. <a href="https://gpuopen.com/download/Real-Time_Procedural_Generation_with_GPU_Work_Graphs-GPUOpen_preprint.pdf" target="_blank" rel="noreferrer">Paper</a>. GPU work graphs generating 79K+ vegetation instances in under 4ms.</p>
<h3 id="browser-gpu-technology" tabindex="-1">Browser GPU Technology <a class="header-anchor" href="#browser-gpu-technology" aria-label="Permalink to &quot;Browser GPU Technology&quot;"></a></h3>
<p><strong>&quot;GSWT Renderer&quot;</strong> -- SIGGRAPH Asia 2025. <a href="https://github.com/zengyf131/gswt_renderer" target="_blank" rel="noreferrer">GitHub</a>. WebGPU + Rust/Wasm renderer using Gaussian Splatting Wang Tiles for infinite 3D terrain with dynamic LOD and streaming.</p>
<p><strong>&quot;GPU Compute in the Browser at the Speed of Native: WebGPU Marching Cubes&quot;</strong> -- Usher (2024). <a href="https://www.willusher.io/graphics/2024/04/22/webgpu-marching-cubes/" target="_blank" rel="noreferrer">Blog</a>. Demonstrates that WebGPU compute achieves native-speed performance for parallel mesh generation algorithms.</p>
<h3 id="voxel-rendering-and-large-scale-scenes" tabindex="-1">Voxel Rendering and Large-Scale Scenes <a class="header-anchor" href="#voxel-rendering-and-large-scale-scenes" aria-label="Permalink to &quot;Voxel Rendering and Large-Scale Scenes&quot;"></a></h3>
<p><strong>&quot;Aokana: A GPU-Driven Voxel Rendering Framework for Open World Games&quot;</strong> -- 2025. <a href="https://arxiv.org/abs/2505.02017" target="_blank" rel="noreferrer">arXiv:2505.02017</a>. Sparse Voxel DAG with LOD and streaming for scenes with tens of billions of voxels. 9x memory reduction and 4.8x faster rendering than prior state-of-the-art. Designed for integration with game engines.</p>
<h3 id="procedural-geometry-and-resurfacing" tabindex="-1">Procedural Geometry and Resurfacing <a class="header-anchor" href="#procedural-geometry-and-resurfacing" aria-label="Permalink to &quot;Procedural Geometry and Resurfacing&quot;"></a></h3>
<p><strong>&quot;Real-time Procedural Resurfacing using GPU Mesh Shaders&quot;</strong> -- Raad et al. (Eurographics 2025). <a href="https://hal.science/hal-05176868v1/file/Paper_Resurfacing_2024.pdf" target="_blank" rel="noreferrer">Paper</a>. Generates detailed geometric surfaces from coarse control meshes at render time using mesh shaders. Enables dynamic LOD without storing high-resolution geometry in memory.</p>
<h3 id="terrain-shadows" tabindex="-1">Terrain Shadows <a class="header-anchor" href="#terrain-shadows" aria-label="Permalink to &quot;Terrain Shadows&quot;"></a></h3>
<p><strong>&quot;Optimizing Terrain Shadows&quot;</strong> -- AMD GPUOpen. <a href="https://gpuopen.com/learn/optimizing-terrain-shadows/" target="_blank" rel="noreferrer">Blog</a>. Practical CSM optimization for large-scale terrain. Covers cascade splitting, GPU-efficient shadow map rendering, and terrain-specific optimizations.</p>
<h3 id="virtual-geometry-and-mesh-optimization" tabindex="-1">Virtual Geometry and Mesh Optimization <a class="header-anchor" href="#virtual-geometry-and-mesh-optimization" aria-label="Permalink to &quot;Virtual Geometry and Mesh Optimization&quot;"></a></h3>
<p><strong>&quot;Nanite WebGPU&quot;</strong> -- Scthe (2024). <a href="https://github.com/Scthe/nanite-webgpu" target="_blank" rel="noreferrer">GitHub</a>, <a href="https://scthe.github.io/nanite-webgpu/" target="_blank" rel="noreferrer">Demo</a>. Complete browser implementation of UE5 Nanite architecture: meshlet LOD hierarchy, software rasterizer in WGSL, per-meshlet culling, billboard impostors.</p>
<p><strong>&quot;Billions of Triangles in Minutes&quot;</strong> -- Kapoulkine (2025). <a href="https://zeux.io/2025/09/30/billions-of-triangles-in-minutes/" target="_blank" rel="noreferrer">Blog</a>. Meshoptimizer v1.0 for hierarchical clustered LOD generation. Processes massive meshes into Nanite-style cluster DAGs efficiently.</p>
<p><strong>&quot;Billboard Splatting (BBSplat)&quot;</strong> -- 2024. <a href="https://arxiv.org/abs/2411.08508" target="_blank" rel="noreferrer">arXiv:2411.08508</a>. Learnable textured planar primitives for novel view synthesis achieving 17x compression vs 3D Gaussian Splatting.</p>
<h3 id="terrain-lighting-and-global-illumination" tabindex="-1">Terrain Lighting and Global Illumination <a class="header-anchor" href="#terrain-lighting-and-global-illumination" aria-label="Permalink to &quot;Terrain Lighting and Global Illumination&quot;"></a></h3>
<p><strong>&quot;Global Illumination in Once Human&quot;</strong> -- GDC 2025. <a href="https://schedule.gdconf.com/session/global-illumination-in-once-human-a-hybrid-approach-for-16km-open-world/907269" target="_blank" rel="noreferrer">Session</a>. Hybrid GI for 16km open world: neural network compressed probes (69:1 ratio), ML-based indoor/outdoor leak resolution, dynamic probe reactions.</p>
<p><strong>&quot;GI with AMD FidelityFX Brixelizer&quot;</strong> -- GDC 2024. <a href="https://gpuopen.com/download/GDC2024_GI_with_AMD_FidelityFX_Brixelizer.pdf" target="_blank" rel="noreferrer">Paper</a>. Compute-based sparse distance field cascades with screen-space probes. No hardware ray-tracing required.</p>
<p><strong>&quot;Radiance Cascades&quot;</strong> -- 2024. <a href="https://jason.today/rc" target="_blank" rel="noreferrer">Blog</a>. Noiseless real-time global illumination using cascaded radiance structures without temporal accumulation.</p>
<h3 id="level-design-and-exploration" tabindex="-1">Level Design and Exploration <a class="header-anchor" href="#level-design-and-exploration" aria-label="Permalink to &quot;Level Design and Exploration&quot;"></a></h3>
<p><strong>&quot;PlotMap: Automated Layout Design for Building Game Worlds&quot;</strong> -- 2023. <a href="https://arxiv.org/html/2309.15242v4" target="_blank" rel="noreferrer">arXiv:2309.15242</a>. AI-assisted POI placement satisfying narrative spatial constraints on terrain.</p>
<p><strong>&quot;Spatial Exploration Triggers&quot;</strong> -- Purdue University (FDG 2022). <a href="https://web.ics.purdue.edu/~cmousas/papers/conf22-FDG-SpatialExploration.pdf" target="_blank" rel="noreferrer">Paper</a>. Four design patterns that drive player exploration: extreme points, visual obstructions, out-of-place objects, spatial connections.</p>
<h3 id="data-compression-and-streaming" tabindex="-1">Data Compression and Streaming <a class="header-anchor" href="#data-compression-and-streaming" aria-label="Permalink to &quot;Data Compression and Streaming&quot;"></a></h3>
<p><strong>&quot;Entropy-driven Progressive Compression of 3D Point Clouds&quot;</strong> -- SGP 2024. <a href="https://inria.hal.science/hal-04630545v1/file/sgp2024.pdf" target="_blank" rel="noreferrer">Paper</a>. Rate-distortion optimized progressive compression using recursive space partitioning with adaptive quantization. Produces refinement streams suited to variable-bandwidth network streaming.</p>
<h3 id="interactive-editing" tabindex="-1">Interactive Editing <a class="header-anchor" href="#interactive-editing" aria-label="Permalink to &quot;Interactive Editing&quot;"></a></h3>
<p><strong>&quot;WebGPU SDF Editor&quot;</strong> -- Nijhoff (2026). <a href="https://reindernijhoff.net/2026/01/webgpu-sdf-editor-real-time-signed-distance-field-modeling/" target="_blank" rel="noreferrer">Project</a>. Full-featured SDF modeling in the browser with real-time marching cubes, boolean operations, smooth blending, and octree space partitioning. 112 bytes per primitive.</p>
<h3 id="river-and-coastal-generation" tabindex="-1">River and Coastal Generation <a class="header-anchor" href="#river-and-coastal-generation" aria-label="Permalink to &quot;River and Coastal Generation&quot;"></a></h3>
<p><strong>&quot;Procedural River Drainage Basins&quot;</strong> -- Patel (Red Blob Games). <a href="https://www.redblobgames.com/x/1723-procedural-river-growing/" target="_blank" rel="noreferrer">Project</a>. Drainage-first river generation using Voronoi/triangle mesh edge classification. Builds river hierarchies before assigning terrain elevation.</p>
<p><strong>&quot;NEWTS1.0: Numerical Model of Coastal Erosion by Waves and Transgressive Scarps&quot;</strong> -- MIT (2024). <a href="https://dspace.mit.edu/handle/1721.1/157677" target="_blank" rel="noreferrer">Paper</a>. Simplified coastal erosion model using uniform retreat and wave-driven erosion. Generates headlands, bays, sea stacks, and arches matching real geomorphology.</p>
<h2 id="further-reading" tabindex="-1">Further Reading <a class="header-anchor" href="#further-reading" aria-label="Permalink to &quot;Further Reading&quot;"></a></h2>
<ul>
<li><a href="/guides/browser-3d-open-world-tech.html">Browser 3D Open World Tech</a> covers the full architecture for a multiplayer creator world in the browser</li>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> covers WebGL, WebGPU, and WebAssembly fundamentals</li>
<li><a href="/guides/threejs-usdc-tech-report.html">Three.js + USDC Tech Report</a> on loading 3D assets in the browser</li>
<li><a href="/guides/frontier-gen-ai-models.html">Frontier Open-Source Gen AI Models</a> for the AI generation pipeline</li>
<li><a href="/tutorials/webgpu-getting-started.html">WebGPU getting started</a> — compute shaders for GPU-driven terrain</li>
<li><a href="/tutorials/web-workers-game-logic.html">Web Workers for game logic</a> — offloading terrain generation to Workers</li>
<li><a href="/tutorials/game-physics-libraries.html">Game physics libraries</a> — physics for terrain interaction and heightfield colliders</li>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> — texture and material sources for terrain rendering</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Browser 3D Open World Tech for Multiplayer Creator Worlds]]></title>
            <link>https://app.cinevva.com/guides/browser-3d-open-world-tech</link>
            <guid>https://app.cinevva.com/guides/browser-3d-open-world-tech</guid>
            <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Deep research into the rendering, networking, streaming, and world-building tech needed to put creators in a shared browser-based 3D open world they can shape together.]]></description>
            <content:encoded><![CDATA[<h1 id="browser-3d-open-world-tech-for-multiplayer-creator-worlds" tabindex="-1">Browser 3D Open World Tech for Multiplayer Creator Worlds <a class="header-anchor" href="#browser-3d-open-world-tech-for-multiplayer-creator-worlds" aria-label="Permalink to &quot;Browser 3D Open World Tech for Multiplayer Creator Worlds&quot;"></a></h1>
<p>We want to put our creators in a shared open world. Not a lobby. Not a gallery. A living 3D space where people can explore, build, and stumble onto each other's work. Something that loads in a browser tab and feels like a place worth being in.</p>
<p>That's a hard problem. Skyrim and The Witcher 3 spent hundreds of millions of dollars building their worlds, and they still ship on dedicated hardware with tens of gigabytes on disk. We're targeting a browser tab, shared state across hundreds of players, and a world that creators can actually reshape.</p>
<p>This guide documents what we found researching the tech. What's possible today, what's coming, and what architecture would get us there.</p>
<h2 id="what-we-can-learn-from-skyrim-and-the-witcher" tabindex="-1">What We Can Learn from Skyrim and The Witcher <a class="header-anchor" href="#what-we-can-learn-from-skyrim-and-the-witcher" aria-label="Permalink to &quot;What We Can Learn from Skyrim and The Witcher&quot;"></a></h2>
<p>Before picking technology, it helps to understand how the two most successful open worlds actually work. Their techniques map surprisingly well to browser constraints.</p>
<h3 id="skyrim-s-cell-system" tabindex="-1">Skyrim's Cell System <a class="header-anchor" href="#skyrim-s-cell-system" aria-label="Permalink to &quot;Skyrim's Cell System&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/JSRtYpNRoN0" title="Skyrim Special Edition Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Skyrim's open world streams a 5x5 grid of cells around the player. Distant mountains are low-resolution impostors. You never notice.</div>
<p>Skyrim divides its world into a grid of 57x37 exterior cells, each 4096x4096 game units (roughly 59 meters). The engine loads a 5x5 grid of cells around the player at any time. As you walk, cells on the trailing edge unload while cells on the leading edge stream in. Interior spaces (dungeons, buildings) are separate worldspaces loaded through door triggers.</p>
<p>This is directly applicable to a browser world. You don't load the entire map. You load a neighborhood around the player and swap chunks as they move. The key insight: Skyrim never lets you see the full detail of distant terrain. It uses a multi-resolution approach.</p>
<p><strong>LOD (Level of Detail) tiers in Skyrim:</strong></p>
<ul>
<li>Full detail within 1-2 cells (player's immediate area)</li>
<li>Simplified meshes at medium range (objects lose small geometry)</li>
<li>Billboard impostors for trees and rocks beyond medium range</li>
<li>Terrain LOD uses pre-baked low-resolution meshes for distant landscape</li>
<li>Object fade distances tuned per-object (grass fades first, buildings last)</li>
</ul>
<p>Skyrim's landscape uses a heightmap-based terrain with 32x32 vertex patches per cell. Each patch can have different textures blended with alpha maps. This is cheap to store and render. A single cell's terrain data is measured in kilobytes, not megabytes.</p>
<p><strong>What we can apply:</strong> Chunk-based streaming, heightmap terrain, aggressive LOD, interior/exterior separation, and the idea that distant terrain doesn't need to be geometrically accurate, just visually convincing.</p>
<h3 id="the-witcher-3-s-streaming-architecture" tabindex="-1">The Witcher 3's Streaming Architecture <a class="header-anchor" href="#the-witcher-3-s-streaming-architecture" aria-label="Permalink to &quot;The Witcher 3's Streaming Architecture&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/53MyR_Z3i1w" title="The Witcher 3: Wild Hunt - Beautiful Open World" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">The Witcher 3's 136 km2 world uses content-aware streaming. Trees beyond 200m are flat impostors. Buildings stream independently from terrain.</div>
<p>The Witcher 3's world is larger than Skyrim (roughly 136 km2 across all regions) and uses a more sophisticated streaming system. CD Projekt RED built a custom engine (REDengine 3) where the world is divided into streaming sectors, not a strict grid.</p>
<p>Each sector contains a hierarchy of content layers:</p>
<ul>
<li><strong>Terrain</strong> streams as patches with 4 LOD levels</li>
<li><strong>Foliage</strong> uses GPU instancing with distance-based culling</li>
<li><strong>Static geometry</strong> (buildings, rocks) streams independently from terrain</li>
<li><strong>Gameplay objects</strong> (NPCs, items, triggers) load based on quest state and proximity</li>
</ul>
<p>The renderer uses deferred shading with physically-based materials. One trick that's particularly relevant: The Witcher 3 aggressively uses impostors. A tree that's 200 meters away isn't a 3D model with branches and leaves. It's a flat textured quad that always faces the camera. The engine pre-renders these impostors from multiple angles and swaps them as the camera moves. You never notice because they're far enough away.</p>
<p><strong>World composition:</strong> The Witcher 3's open world was built in layers by separate teams working simultaneously. The terrain team sculpted the landscape. The environment art team placed structures. The quest team wired up triggers and NPC paths. This layered composition system let a large team work without stepping on each other.</p>
<p><strong>What we can apply:</strong> Layered world composition (critical for a creator platform), impostor rendering for distant objects, deferred rendering for many light sources, and the insight that streaming should be content-aware (don't load interiors you can't see, don't spawn NPCs you're not near).</p>
<h3 id="breath-of-the-wild-s-chemistry-engine" tabindex="-1">Breath of the Wild's Chemistry Engine <a class="header-anchor" href="#breath-of-the-wild-s-chemistry-engine" aria-label="Permalink to &quot;Breath of the Wild's Chemistry Engine&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/zw47_q9wbBE" title="Zelda: Breath of the Wild - Nintendo Switch Presentation Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">BotW runs on tablet-class hardware and looks stunning. Cel-shaded style, watercolor distance fog, and systemic interactions over scripted content. The gold standard for stylized open worlds.</div>
<p>Nintendo's approach to open world design inverted the typical formula. Instead of filling the world with scripted content, they built systemic rules and let players discover emergent behavior. Fire spreads to grass. Metal conducts electricity in thunderstorms. Wind carries objects. Everything interacts with everything else through a consistent physics/chemistry layer.</p>
<p>This matters for a creator world because it suggests that the world itself can be more interesting than any individual placed object. If creators can define material properties and interaction rules (not just place static meshes), the world gains emergent behavior that makes exploration worthwhile even in areas nobody explicitly designed.</p>
<p><strong>Technical takeaways from BotW:</strong></p>
<ul>
<li>Relatively low polygon count and resolution (900p docked on Switch). The stylized cel-shaded look hides the low fidelity. A browser world could achieve similar visual quality today.</li>
<li>Distance rendering uses a toon-shaded fog that fades into watercolor-style skyboxes. Cheap to render, beautiful in practice. This kind of atmospheric perspective is essentially free in a fragment shader.</li>
<li>The world is roughly 84 km2 but sparse. Most of the map is traversable terrain with points of interest spread across it. Dense content is reserved for towns and shrines. This &quot;sparse but interesting&quot; approach reduces asset requirements dramatically compared to The Witcher 3's densely populated Novigrad.</li>
<li>Physics objects have material tags (wood, metal, food, explosive) that determine interactions. The number of actual rules is small (maybe 20-30 interaction types) but they combine in ways that feel infinite.</li>
</ul>
<h3 id="gta-v-s-world-layering" tabindex="-1">GTA V's World Layering <a class="header-anchor" href="#gta-v-s-world-layering" aria-label="Permalink to &quot;GTA V's World Layering&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/QkkoHAzjnUs" title="Grand Theft Auto V Official Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">GTA V's Los Santos feels alive because of layered ambient systems: traffic, pedestrians, time-of-day, weather. Distant buildings are composited photographs, not 3D models.</div>
<p>Rockstar's approach to Los Santos teaches a different lesson. GTA V's world feels alive because of layered ambient systems, not because every NPC has a quest.</p>
<p>The city has traffic systems that simulate hundreds of vehicles on a road network. Pedestrians walk routes, react to the player, and interact with each other. Time of day changes the population density, the types of NPCs present, and the ambient activity. Weather affects driving physics and NPC behavior.</p>
<p>For a creator world, the insight is that ambient life makes the difference between a world that feels like a museum and one that feels like a place. Even simple behaviors (birds flying overhead, waves lapping at a shore, NPCs walking between buildings) create the illusion of a living world.</p>
<p><strong>Technical takeaways from GTA V:</strong></p>
<ul>
<li>The map is 75 km2 and uses aggressive LOD combined with a streaming system that loads based on velocity (driving fast loads further ahead than walking).</li>
<li>GTA Online puts 30 players in this world simultaneously. Even at 30, Rockstar found they needed spatial interest management: you get detailed updates about nearby players and less frequent updates about distant ones. The same principle applies at our 200-player target.</li>
<li>GTA V's asset pipeline is one of the most efficient ever built. The entire game fits in roughly 80 GB because of extreme texture compression, mesh sharing, and procedural detail. Their building interiors reuse modular pieces extensively.</li>
<li>The game uses a sophisticated imposter system where distant buildings are actually flat photographs composited together. The player never notices because the transition distances are tuned to specific viewing angles.</li>
</ul>
<h3 id="elden-ring-s-seamless-open-world" tabindex="-1">Elden Ring's Seamless Open World <a class="header-anchor" href="#elden-ring-s-seamless-open-world" aria-label="Permalink to &quot;Elden Ring's Seamless Open World&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/E3Huy2cdih0" title="Elden Ring Official Gameplay Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Elden Ring's 79 km2 map mixes sparse open terrain with dense hand-crafted dungeons. The same environmental assets appear everywhere in different arrangements. Repetition disappears when composition and lighting vary.</div>
<p>FromSoftware built Elden Ring on the same engine as Dark Souls but scaled it to an open world. The result is interesting because it shows how a relatively small team (compared to Rockstar or CD Projekt) can build a large open world.</p>
<p>The trick is density variation. Elden Ring's map is huge (roughly 79 km2) but large sections are open terrain with scattered enemies and points of interest. The dense, hand-crafted content (Legacy Dungeons like Stormveil Castle) is embedded in the open world and uses the same interior/exterior separation that Skyrim uses.</p>
<p><strong>Technical takeaways from Elden Ring:</strong></p>
<ul>
<li>The world streams in large tiles. On horseback you can see very far, but the distant terrain is extremely simplified. Close up, the detail is comparable to Dark Souls 3.</li>
<li>Loading screens only appear when fast-traveling or entering certain dungeons. The open world itself streams without interruption.</li>
<li>The game reuses environmental assets extensively. The same tree models, rock formations, and ruin pieces appear across the map in different arrangements. With enough variety in arrangement and lighting, repetition isn't noticeable. This is directly applicable to a creator world where a library of modular pieces can generate infinite variety.</li>
<li>Multiplayer is session-based (summoning other players into your world), not persistent. But the asynchronous features (messages, bloodstains, ghosts of other players) create a sense of shared existence without the networking overhead of a persistent server. These ambient multiplayer features would be cheap to add to a browser world.</li>
</ul>
<h3 id="no-man-s-sky-procedural-everything" tabindex="-1">No Man's Sky: Procedural Everything <a class="header-anchor" href="#no-man-s-sky-procedural-everything" aria-label="Permalink to &quot;No Man's Sky: Procedural Everything&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/nLtmEjqzg7M" title="No Man's Sky NEXT Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">18 quintillion planets generated from shared seeds. Every player sees the same universe without storing it on a server. Base building data is compact: part IDs plus transforms.</div>
<p>No Man's Sky is the extreme case of procedural generation. 18 quintillion planets generated from a shared seed, so every player sees the same universe without any of it being stored on a server.</p>
<p>The generation pipeline works in stages: galaxy-level seeds determine star positions, star seeds determine planet count and types, planet seeds drive terrain generation (multi-octave noise), biome assignment, flora/fauna species, color palettes, and resource distribution. Everything computes deterministically from the seed, so two players visiting the same coordinates see the same planet without exchanging any planet data.</p>
<p><strong>Technical takeaways from No Man's Sky:</strong></p>
<ul>
<li>Terrain generation uses voxel-based marching cubes with a stack of noise functions. This allows caves, overhangs, and floating islands that heightmap terrain can't represent. The trade-off is higher compute cost per chunk, but the result is more visually interesting terrain.</li>
<li>The base-building system is closest to what we're envisioning. Players place structures that persist on the server and are visible to other players. The base data is compact (a list of parts with positions and rotations) and streams on demand when someone visits the planet.</li>
<li>The game was initially criticized for repetitive content despite infinite variety. Seed-based generation can produce infinite terrain but limited surprises. The lesson: procedural generation works for the canvas, but creator-placed content is what makes a place feel designed and intentional.</li>
<li>Hello Games added multiplayer years after launch. Up to 32 players share a session with full building and exploration. The network model is simple: one player hosts, others connect. For a browser world with persistent state, a server-authoritative model works better, but No Man's Sky proves that shared procedural worlds are tractable.</li>
</ul>
<h3 id="minecraft-the-blueprint-for-creator-worlds" tabindex="-1">Minecraft: The Blueprint for Creator Worlds <a class="header-anchor" href="#minecraft-the-blueprint-for-creator-worlds" aria-label="Permalink to &quot;Minecraft: The Blueprint for Creator Worlds&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/MmB9b5njVbA" title="Minecraft Official Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">300 million copies sold. 16x16 pixel textures. Fully editable terrain. Infinitely generated. Multiplayer. The most important reference for a creator world. Browser clones prove the concept works in WebGL.</div>
<p>Minecraft is the most important reference point for what we're building, more than any graphical AAA title. 300 million copies sold. The world is infinitely generated, fully destructible, and multiplayer. Creators don't just place objects in Minecraft. They reshape the terrain itself.</p>
<p><strong>Technical takeaways from Minecraft:</strong></p>
<ul>
<li>The world is divided into 16x16x384 block chunks. Only chunks near the player are loaded (render distance is configurable). This is the same chunk streaming pattern as Skyrim but with fully editable terrain.</li>
<li>Each chunk is stored as a palette-compressed array of block IDs. A chunk with only 5 different block types stores a 4-bit palette index per block instead of a full block ID. This makes chunk data very compact (typically 10-50 KB per chunk after compression).</li>
<li>Minecraft's multiplayer protocol is well-documented and relatively simple. The server sends chunk data as the player moves. Block changes are broadcast as small delta updates (position + new block type). This is exactly the model we'd use for creator edits.</li>
<li>The game runs in Java and now has a Bedrock Edition in C++. There are browser-based Minecraft clones (ClassiCube, eaglercraft) that prove the core concept works in WebGL. They typically handle 8-12 chunk render distance at 60fps, which gives a view of about 200-400 meters.</li>
<li>Minecraft's modding ecosystem is its real moat. Mods add new blocks, entities, biomes, and gameplay systems. For a creator world, this suggests that extensibility matters as much as the base experience. If creators can define new interaction types (not just place objects), the world gets richer over time.</li>
<li>Redstone (Minecraft's in-game wiring system) shows that creators will build complex systems if you give them simple, composable primitives. Logic gates, automatic farms, calculators. The simple rule set generates extraordinary complexity. This is the same lesson as BotW's chemistry engine.</li>
</ul>
<h3 id="common-patterns-across-aaa-open-worlds" tabindex="-1">Common Patterns Across AAA Open Worlds <a class="header-anchor" href="#common-patterns-across-aaa-open-worlds" aria-label="Permalink to &quot;Common Patterns Across AAA Open Worlds&quot;"></a></h3>
<p>Looking across all these titles, the same patterns keep showing up:</p>
<p><strong>Spatial partitioning</strong> is universal. Whether it's cells (Bethesda), sectors (CD Projekt), chunks (Mojang/Rockstar), or tiles (FromSoftware), every open world divides space into loadable units. No engine tries to hold the entire world in memory.</p>
<p><strong>Multi-resolution everything.</strong> Terrain, meshes, textures, and even audio all have multiple quality levels. The resolution you get depends on how close you are and how important the object is.</p>
<p><strong>Occlusion culling</strong> matters more than raw triangle throughput. Not rendering what you can't see saves more performance than optimizing what you can. Skyrim uses a simple distance-based system. The Witcher 3 uses software occlusion with large occluders (buildings, cliffs). Modern engines like UE5's Nanite take this further with hardware occlusion queries.</p>
<p><strong>Async loading</strong> hides transitions. These games don't show loading screens for the open world (only for fast-travel or interior transitions). They load content on background threads, decompress in parallel, and swap in new content gradually.</p>
<p><strong>Art direction over polygon count.</strong> Skyrim shipped in 2011 with graphics that were modest even then. BotW runs on a tablet-class chip and looks beautiful. Minecraft uses 16x16 pixel textures and is one of the most visually recognizable games ever made. For a browser world, this matters enormously. A well-directed art style at lower fidelity will always beat a technically advanced but artistically flat world.</p>
<p><strong>Systemic design beats scripted content.</strong> BotW's chemistry engine, Minecraft's block interactions, and GTA V's traffic systems all create emergent behavior from simple rules. This is cheaper to build, cheaper to run, and generates more player stories than hand-crafted scripted sequences. For a creator world, systemic design means the world stays interesting even in areas nobody explicitly designed.</p>
<p><strong>Creator-placed content needs persistence and visibility.</strong> Minecraft bases, No Man's Sky bases, and GTA Online properties all persist across sessions and are visible to other players. The data model for creator content is always compact (part IDs + transforms) while the visual representation is rich (the client expands the data into full 3D scenes).</p>
<h2 id="rendering-what-can-a-browser-actually-do" tabindex="-1">Rendering: What Can a Browser Actually Do? <a class="header-anchor" href="#rendering-what-can-a-browser-actually-do" aria-label="Permalink to &quot;Rendering: What Can a Browser Actually Do?&quot;"></a></h2>
<h3 id="three-js" tabindex="-1">Three.js <a class="header-anchor" href="#three-js" aria-label="Permalink to &quot;Three.js&quot;"></a></h3>
<p>Three.js is the foundation. It has the largest community (over 100K GitHub stars), the most examples, and the broadest compatibility. It abstracts WebGL 2 and has experimental WebGPU support via <code>WebGPURenderer</code>.</p>
<p>For an open world, Three.js gives you:</p>
<ul>
<li><strong>Instanced rendering</strong> for foliage, rocks, and repeated geometry (<code>InstancedMesh</code>)</li>
<li><strong>LOD system</strong> built in (<code>THREE.LOD</code> swaps meshes by distance)</li>
<li><strong>Frustum culling</strong> automatic per-object</li>
<li><strong>Terrain</strong> via custom <code>BufferGeometry</code> or heightmap-based <code>PlaneGeometry</code></li>
<li><strong>PBR materials</strong> through <code>MeshStandardMaterial</code> and <code>MeshPhysicalMaterial</code></li>
<li><strong>Post-processing</strong> via <code>EffectComposer</code> (bloom, SSAO, tone mapping)</li>
<li><strong>Shadow maps</strong> with cascaded shadow mapping possible through custom code</li>
<li><strong>glTF/GLB</strong> as the primary asset format (compact, GPU-ready)</li>
</ul>
<p>Three.js also has an active ecosystem of tools that matter for open worlds. <code>three-mesh-bvh</code> accelerates raycasting and spatial queries over complex meshes. <code>postprocessing</code> (by vanruesc) provides a more performant post-processing stack than Three's built-in one. <code>three-gpu-pathtracing</code> enables reference-quality rendering.</p>
<p><strong>Limitations for open worlds:</strong> Three.js doesn't have a built-in scene graph that handles streaming, LOD management, or spatial partitioning at scale. You build those yourself. There's no built-in entity component system (ECS), no physics, and no terrain system. It's a renderer, not an engine. That's actually an advantage for a custom open world because you control memory layout and loading strategy, but it means more up-front work.</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> lod</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> THREE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">LOD</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lod.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(highDetailMesh, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lod.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mediumDetailMesh, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">50</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lod.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(lowDetailMesh, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">200</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">lod.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addLevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(impostorSprite, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">500</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">scene.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(lod);</span></span></code></pre>
</div><h3 id="babylon-js" tabindex="-1">Babylon.js <a class="header-anchor" href="#babylon-js" aria-label="Permalink to &quot;Babylon.js&quot;"></a></h3>
<p>Babylon.js is the other major contender. Microsoft-backed, it has deeper built-in features than Three.js for the specific problem of open worlds.</p>
<p>Relevant built-in features:</p>
<ul>
<li><strong>Octree-based scene partitioning</strong> for efficient culling of large scenes</li>
<li><strong>Solid Particle System</strong> for massive instanced rendering</li>
<li><strong>Terrain from heightmaps</strong> with built-in LOD and multi-texture splatting</li>
<li><strong>Node Material Editor</strong> for visual shader creation</li>
<li><strong>WebGPU support</strong> (more mature than Three.js, as Babylon invested earlier)</li>
<li><strong>Havok physics integration</strong> (Wasm-compiled, production-quality)</li>
<li><strong>glTF streaming</strong> with progressive loading</li>
</ul>
<p>Babylon's <code>DynamicTerrain</code> extension generates terrain chunks on-the-fly from heightmap data, handles LOD automatically, and supports texture splatting. This is much closer to what Skyrim does than anything Three.js offers out of the box.</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> terrain</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> BABYLON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">DynamicTerrain</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"terrain"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, {</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  terrainSub: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  mapData: heightmapData,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  mapSubX: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  mapSubZ: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1000</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}, scene);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">terrain.LODLimits </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span></code></pre>
</div><p><strong>The trade-off:</strong> Babylon.js is a larger library (the full build is ~1-2MB minified vs Three.js at ~600KB). But for an open world, you'll likely add enough custom code to Three.js that the size difference becomes negligible. Babylon also has its own Node Material system, inspector, and dev tools, which speed up iteration.</p>
<p>Babylon.js 8.0 (released March 2025) hardened the case for it as the engine core here. All core engine shaders now ship in both GLSL and WGSL, so WebGPU works with no conversion layer and the WebGPU bundle is roughly half the size it used to be. The same release brought Havok's full character controller into the engine, area lights, a rebuilt audio engine, and Gaussian splatting improvements (SPZ and compressed PLY formats, spherical harmonics, and lower memory/CPU footprint).</p>
<h3 id="playcanvas" tabindex="-1">PlayCanvas <a class="header-anchor" href="#playcanvas" aria-label="Permalink to &quot;PlayCanvas&quot;"></a></h3>
<p>PlayCanvas is worth mentioning because it's the most production-proven web-first 3D engine. Snap, Facebook, and numerous advertising clients have shipped complex 3D experiences with it. The engine is around 1MB, loads fast, and the cloud editor enables collaborative world building.</p>
<p>For an open world specifically, PlayCanvas offers batch groups for draw call optimization, a built-in lightmapper, and Gaussian splatting support (relevant for photogrammetry-captured real-world environments). The runtime is tight and well-optimized for mobile browsers.</p>
<h3 id="webgpu-the-performance-unlock" tabindex="-1">WebGPU: The Performance Unlock <a class="header-anchor" href="#webgpu-the-performance-unlock" aria-label="Permalink to &quot;WebGPU: The Performance Unlock&quot;"></a></h3>
<p>WebGPU changes the equation for browser open worlds. The two features that matter most:</p>
<p><strong>Compute shaders</strong> enable GPU-side terrain generation, foliage placement, particle simulation, and even simple physics. In a WebGL world, all of this runs on the CPU in JavaScript. With WebGPU, you can generate terrain patches entirely on the GPU, compute LOD transitions on the GPU, and run culling passes on the GPU. This frees the CPU for networking, game logic, and content streaming.</p>
<p><strong>Indirect rendering</strong> lets the GPU decide what to draw based on compute shader output. You submit one draw call, and the GPU decides how many instances to render for each LOD level based on distance. This is how modern engines handle millions of blades of grass or trees. Without indirect rendering (which WebGL doesn't support), the CPU has to sort and batch everything, which becomes the bottleneck in dense scenes.</p>
<p>WebGPU now ships in every major browser. Chrome and Edge have had it since version 113, Firefox added it on Windows (141) and Apple Silicon macOS (145), and Safari 26 brought it to macOS, iOS, iPadOS, and visionOS in late 2025. That puts global support around 82%, including mobile (Chrome for Android and Samsung Internet ship it too). You'd still keep a WebGL 2 fallback for older browsers and devices where WebGPU isn't enabled, but the gap has closed fast.</p>
<div class="language-wgsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">wgsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">workgroup_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">64</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> generateTerrain</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builtin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(global_invocation_id) id: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">u32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> worldPos </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">x), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">y)) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> cellSize </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> worldOffset;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> height </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fbmNoise</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(worldPos, octaves, persistence);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    heightmap[id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">x </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">y </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> width] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> height;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="recommended-rendering-stack" tabindex="-1">Recommended Rendering Stack <a class="header-anchor" href="#recommended-rendering-stack" aria-label="Permalink to &quot;Recommended Rendering Stack&quot;"></a></h3>
<p>For a browser open world targeting creators on desktop:</p>
<p><strong>Primary:</strong> Three.js or Babylon.js with WebGPU renderer where available, WebGL 2 fallback. Babylon has stronger built-in open-world primitives. Three.js has a larger community and more flexibility.</p>
<p><strong>Our pick:</strong> Babylon.js for the engine core because of built-in terrain LOD, octree culling, Havok physics (Wasm), and mature WebGPU support. Use Three.js ecosystem tools where Babylon lacks them (e.g., mesh BVH for spatial queries). Wrap everything in a custom world-streaming layer.</p>
<h2 id="world-streaming-architecture" tabindex="-1">World Streaming Architecture <a class="header-anchor" href="#world-streaming-architecture" aria-label="Permalink to &quot;World Streaming Architecture&quot;"></a></h2>
<p>This is the hard part. A browser tab gets roughly 2-4 GB of memory on desktop (browser-imposed limits), about 1 GB on mobile, and no direct disk access. Everything comes over the network. You need an architecture that keeps the visible world in memory while streaming content just ahead of where the player is moving.</p>
<h3 id="chunk-based-world-grid" tabindex="-1">Chunk-Based World Grid <a class="header-anchor" href="#chunk-based-world-grid" aria-label="Permalink to &quot;Chunk-Based World Grid&quot;"></a></h3>
<p>Like Skyrim's cell system, divide the world into a regular grid of chunks. Each chunk is an independent unit that can be loaded, rendered, and unloaded separately.</p>
<p><strong>Chunk sizing matters.</strong> Too small and you're constantly loading/unloading with high overhead. Too large and each chunk takes too long to download. For a browser world with typical broadband connections:</p>
<ul>
<li>64x64 meter chunks at ground level</li>
<li>Each chunk contains: heightmap patch (2-4 KB), texture splat map (16-32 KB compressed), static meshes as instanced refs (1-50 KB of instance data), creator-placed objects as a manifest (1-10 KB)</li>
<li>Load radius: 5x5 chunks at full detail (320m view), 9x9 at medium LOD, 17x17 at terrain-only</li>
<li>Target: each chunk's full-detail data under 200 KB, so a 5x5 neighborhood is under 5 MB</li>
</ul>
<h3 id="progressive-loading-pipeline" tabindex="-1">Progressive Loading Pipeline <a class="header-anchor" href="#progressive-loading-pipeline" aria-label="Permalink to &quot;Progressive Loading Pipeline&quot;"></a></h3>
<p>Don't load everything about a chunk at once. Use a priority queue:</p>
<ol>
<li><strong>Terrain geometry first</strong> (heightmap only, 2-4 KB per chunk). The player sees ground within 100ms.</li>
<li><strong>Terrain textures</strong> (splat maps, low-res first then upgrade). Ground has color within 200ms.</li>
<li><strong>Major structures</strong> (buildings, large rocks). Silhouettes appear within 500ms.</li>
<li><strong>Detail objects</strong> (foliage, small props, creator items). World fills in over 1-2 seconds.</li>
<li><strong>High-res textures</strong> upgrade last. Nobody notices if a distant building's texture takes an extra second.</li>
</ol>
<p>This matches how the human eye works. We notice missing ground and missing large structures. We don't notice missing grass.</p>
<h3 id="memory-management" tabindex="-1">Memory Management <a class="header-anchor" href="#memory-management" aria-label="Permalink to &quot;Memory Management&quot;"></a></h3>
<p>Browser memory is finite and the garbage collector is your enemy. A single GC pause can drop you from 60fps to 10fps for a frame.</p>
<p><strong>Object pooling</strong> is mandatory. Pre-allocate pools for common objects (trees, rocks, grass patches) and recycle them as chunks load/unload. Never create new <code>THREE.Mesh</code> or <code>BABYLON.Mesh</code> instances in the hot path. Swap geometry and material references on pooled objects instead.</p>
<p><strong>Texture atlases</strong> reduce both draw calls and memory fragmentation. Pack all terrain textures into a few large atlases. Pack creator-uploaded textures into per-chunk atlases on the server and stream them as single images.</p>
<p><strong>Geometry compression</strong> with Draco or Meshopt reduces download size by 5-10x and decompression runs on a Web Worker so it doesn't block the main thread. For terrain specifically, quantized heightmaps (16-bit values compressed with simple delta encoding) are smaller than any general-purpose mesh format.</p>
<p><strong>ArrayBuffer ownership transfer</strong> between workers and the main thread avoids copying. When a worker decompresses a mesh, transfer the buffer to the main thread with zero copy using <code>postMessage</code> with transferable objects.</p>
<h3 id="asset-delivery" tabindex="-1">Asset Delivery <a class="header-anchor" href="#asset-delivery" aria-label="Permalink to &quot;Asset Delivery&quot;"></a></h3>
<p><strong>CDN with edge caching</strong> for static world data. Terrain chunks, base meshes, and texture atlases that don't change often should be cached aggressively (Cache-Control: max-age=31536000, immutable).</p>
<p><strong>Content-addressed storage</strong> means each asset version gets a unique hash in its URL. When a creator modifies a chunk, the new version gets a new hash and the old one stays in cache for anyone still looking at it. No cache invalidation needed.</p>
<p><strong>KTX2 textures</strong> with Basis Universal compression. These decompress to whatever GPU format the device supports (BC7, ASTC, ETC2, or RGBA fallback). A 1024x1024 terrain texture goes from 4 MB uncompressed to roughly 150 KB in KTX2. For a world with thousands of unique textures, this compression is the difference between viable and not.</p>
<p><strong>glTF Binary (GLB)</strong> for all 3D assets. It's the JPEG of 3D. Every browser engine loads it, it's compact, and it can embed textures, materials, and animations in a single file. Use Draco or Meshopt extensions for mesh compression. Creator-uploaded assets get processed server-side into optimized GLB before entering the world.</p>
<h2 id="multiplayer-networking" tabindex="-1">Multiplayer Networking <a class="header-anchor" href="#multiplayer-networking" aria-label="Permalink to &quot;Multiplayer Networking&quot;"></a></h2>
<p>Putting hundreds of creators in the same world requires a networking architecture that handles real-time movement, persistent world state, and creator edits without melting.</p>
<h3 id="server-architecture" tabindex="-1">Server Architecture <a class="header-anchor" href="#server-architecture" aria-label="Permalink to &quot;Server Architecture&quot;"></a></h3>
<p><strong>Authoritative server</strong> for world state. The browser is untrusted. All meaningful actions (placing objects, modifying terrain, moving between chunks) are validated server-side. The client predicts locally and reconciles with server state.</p>
<p><strong>Spatial sharding</strong> divides the world across server instances. Each shard owns a rectangular region of the world grid. As player density shifts, shards can split or merge. This is how EVE Online handles thousands of players in one universe, and it's the same principle at smaller scale.</p>
<p>For a creator world where most interactions are local (you're building in your area, your neighbors can see it), spatial sharding works naturally. A player standing at a shard boundary sees both shards' content, which requires cross-shard visibility queries, but this is a solved problem.</p>
<p><strong>Technology options for the server:</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>Technology</th>
<th>Strengths</th>
<th>Use Case</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Cloudflare Durable Objects</strong></td>
<td>Edge-deployed, auto-scaling, built-in persistence, WebSocket support</td>
<td>World shard state, per-chunk authority</td>
</tr>
<tr>
<td><strong>Hathora / Rivet</strong></td>
<td>Managed game server hosting, DDoS protection, global deployment</td>
<td>Dedicated game server instances</td>
</tr>
<tr>
<td><strong>Colyseus</strong></td>
<td>Open-source game server framework for Node.js, schema-based state sync</td>
<td>Room-based multiplayer with state diffing</td>
</tr>
<tr>
<td><strong>PartyKit</strong></td>
<td>Edge-deployed, WebSocket + WebRTC, Cloudflare Workers based</td>
<td>Real-time collaboration, lightweight multiplayer</td>
</tr>
<tr>
<td><strong>Custom Rust/Go</strong></td>
<td>Maximum control, best performance per instance</td>
<td>High-density shards needing low-latency physics</td>
</tr>
</tbody>
</table>
<p><strong>Our context (Cloudflare infrastructure):</strong> Durable Objects are a natural fit. Each world chunk becomes a Durable Object that holds authoritative state for that chunk's content. Players connect via WebSocket to the Durable Object responsible for their current chunk. When they move to an adjacent chunk, they connect to that chunk's DO. Durable Objects persist state to disk automatically, so world data survives restarts.</p>
<h3 id="client-server-communication" tabindex="-1">Client-Server Communication <a class="header-anchor" href="#client-server-communication" aria-label="Permalink to &quot;Client-Server Communication&quot;"></a></h3>
<p><strong>WebSocket</strong> for reliable ordered messages (chat, world edits, inventory, game state). One connection per active shard the player can see (typically 1-4 connections).</p>
<p><strong>WebRTC DataChannel</strong> for unreliable unordered messages (player positions, animations, ephemeral effects). WebRTC is peer-to-peer capable, but for a world with many players, you'd run it through an SFU (Selective Forwarding Unit) to avoid N2 connections. Cloudflare Calls or LiveKit can serve as the SFU.</p>
<p><strong>State synchronization</strong> uses delta compression. The server tracks what each client has seen and only sends changes. For a creator world, this is particularly important because the world state (what objects exist, where they are, what properties they have) changes much less frequently than player positions. You can afford to send world state updates at 2-5 Hz while player positions update at 20-30 Hz.</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> WorldChunkState</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  version</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  terrain</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TerrainPatch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  objects</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PlacedObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  creators</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreatorPresence</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> DeltaUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  chunkId</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  fromVersion</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  toVersion</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  addedObjects</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> PlacedObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  removedObjectIds</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  modifiedObjects</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Partial</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">PlacedObject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>[];</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">  creatorMoves</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CreatorPosition</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="conflict-resolution-for-creator-edits" tabindex="-1">Conflict Resolution for Creator Edits <a class="header-anchor" href="#conflict-resolution-for-creator-edits" aria-label="Permalink to &quot;Conflict Resolution for Creator Edits&quot;"></a></h3>
<p>When two creators modify the same area simultaneously, you need a conflict resolution strategy. This is where the choice between real-time collaboration models matters.</p>
<p><strong>Last-write-wins</strong> is the simplest. Each object has a single owner at any time. If you're editing a building, nobody else can edit it until you release it. Simple, no conflicts, but limits collaboration.</p>
<p><strong>Operational Transform (OT)</strong> is what Google Docs uses. Operations are transformed against concurrent operations to produce a consistent result. This works for text but gets complex for 3D spatial operations. Figma uses a variant of this for their 2D canvas.</p>
<p><strong>CRDTs (Conflict-free Replicated Data Types)</strong> allow concurrent edits that always converge to the same state without coordination. For a world with discrete objects (each with an ID and properties), a Last-Writer-Wins Register per property combined with an Add-Wins Set for the object collection gives you automatic convergence. Yjs and Automerge are production-quality CRDT libraries for JavaScript.</p>
<p><strong>Our recommendation:</strong> Use CRDTs for world object state (what exists, where it is, what properties it has) and authoritative server for spatial validation (no two objects in the same spot, objects stay within world bounds). The CRDT handles the collaboration. The server handles the physics.</p>
<h3 id="handling-scale-how-many-players" tabindex="-1">Handling Scale: How Many Players? <a class="header-anchor" href="#handling-scale-how-many-players" aria-label="Permalink to &quot;Handling Scale: How Many Players?&quot;"></a></h3>
<p>Browser MMOs exist today. Hordes.io runs 200+ players in one scene in a browser. BrowserQuest (Mozilla's experiment) handled hundreds with a simple tile-based world. The question isn't whether browsers can handle multiplayer, it's what visual fidelity you can maintain as player count rises.</p>
<p><strong>Player rendering budget:</strong> Each visible player needs a mesh, animations, and potentially creator-customized appearance. At 60fps, you have 16ms per frame. A reasonable budget:</p>
<ul>
<li>50 fully-animated players at close range: ~2ms of animation + skinning</li>
<li>200 players at medium range (simplified animation, instanced): ~1ms</li>
<li>500+ players as dots/icons on minimap: negligible</li>
</ul>
<p>This gives you a visible population of ~250 players in one view, which is more than enough for a creator world. World of Warcraft capitals rarely render more than 200 characters in view at once.</p>
<p><strong>Network budget:</strong> Each player sending position at 20 Hz is roughly 40 bytes * 20 = 800 bytes/second. 200 players in view: 160 KB/s of position data. Add world state, chat, and creator actions, and you're looking at 200-500 KB/s per client. Well within broadband capabilities but worth compressing.</p>
<h2 id="terrain-system" tabindex="-1">Terrain System <a class="header-anchor" href="#terrain-system" aria-label="Permalink to &quot;Terrain System&quot;"></a></h2>
<p>Terrain is the foundation of any open world. It's also where browser constraints hit hardest because terrain needs to be everywhere and always visible.</p>
<h3 id="heightmap-based-terrain" tabindex="-1">Heightmap-Based Terrain <a class="header-anchor" href="#heightmap-based-terrain" aria-label="Permalink to &quot;Heightmap-Based Terrain&quot;"></a></h3>
<p>Like Skyrim, use a heightmap. A 2D grid of height values generates 3D terrain through a vertex shader. This is dramatically more compact than arbitrary mesh terrain.</p>
<p>A 4096x4096 heightmap at 16-bit precision is 32 MB uncompressed. But you never load it all at once. Each 64m chunk uses a 65x65 section of the heightmap (about 8.4 KB at 16-bit). Compress that with delta encoding and zlib and you're under 2 KB per chunk.</p>
<p><strong>Texture splatting</strong> paints multiple terrain materials (grass, rock, dirt, sand) using a blend map. Each chunk has a 4-channel RGBA splat map where each channel controls the blend weight of one material. With 4 textures per splat map and the ability to vary splat maps per chunk, you get visual variety across the entire world.</p>
<p>Modern terrain renderers use <strong>virtual texturing</strong> (also called megatexture, from id Software's tech in Rage). Instead of splatting at runtime, you pre-render the blended terrain texture at high resolution and stream tiles of it as the camera moves. This trades storage for runtime performance. WebGPU's compute shaders can handle the feedback and page-table management that virtual texturing requires.</p>
<h3 id="clipmap-or-geoclipmapping" tabindex="-1">Clipmap or Geoclipmapping <a class="header-anchor" href="#clipmap-or-geoclipmapping" aria-label="Permalink to &quot;Clipmap or Geoclipmapping&quot;"></a></h3>
<p>For rendering large terrain in a browser, the CDLOD (C. Dick's LOD) or geoclipmapping approach works well. The terrain is rendered as a set of concentric rings around the camera, each ring at half the resolution of the previous one. Close to the camera you see full-resolution terrain. Far away you see a coarser version. The transitions are smooth because geometry morphs between levels.</p>
<p>This technique is GPU-friendly (one draw call per ring), handles infinite terrain with constant memory, and works in WebGL 2. It's what Flight Simulator and most modern open-world games use at some level.</p>
<h3 id="creator-modified-terrain" tabindex="-1">Creator-Modified Terrain <a class="header-anchor" href="#creator-modified-terrain" aria-label="Permalink to &quot;Creator-Modified Terrain&quot;"></a></h3>
<p>If creators can sculpt terrain, you need a way to store and stream modifications on top of the base heightmap. Two approaches:</p>
<p><strong>Delta heightmaps</strong> store the difference between the base terrain and the modified terrain. Most of the world is unmodified (deltas are zero), so this compresses extremely well. When loading a chunk, apply the delta on top of the base.</p>
<p><strong>Voxel overlays</strong> for more dramatic modifications (caves, overhangs, arches). A heightmap can't represent terrain where one point has two heights. A sparse voxel grid stored only in modified chunks handles this. Marching cubes or dual contouring generates the mesh. This is more expensive but enables Minecraft-style terrain editing.</p>
<h2 id="ai-powered-world-generation" tabindex="-1">AI-Powered World Generation <a class="header-anchor" href="#ai-powered-world-generation" aria-label="Permalink to &quot;AI-Powered World Generation&quot;"></a></h2>
<p>This is where Cinevva's existing generative AI capabilities become a force multiplier. Instead of hand-building every rock and tree, creators can direct AI to populate the world.</p>
<h3 id="terrain-generation-with-neural-fields" tabindex="-1">Terrain Generation with Neural Fields <a class="header-anchor" href="#terrain-generation-with-neural-fields" aria-label="Permalink to &quot;Terrain Generation with Neural Fields&quot;"></a></h3>
<p>Recent research on neural terrain generation (NVIDIA's GET3D, Terragen's neural network mode, and papers like &quot;Terrain Generation Using Procedural Models&quot;) shows that trained models can generate plausible terrain from text prompts or sketch inputs. A creator could draw a rough coastline and say &quot;forested hills meeting a rocky shore&quot; and get a heightmap with appropriate erosion, vegetation masks, and material assignments.</p>
<p>For browser delivery, you'd run the generation server-side and stream the result. The generation model doesn't need to run in the browser. It produces heightmaps and splat maps that the browser can render with the standard terrain pipeline.</p>
<h3 id="_3d-asset-generation" tabindex="-1">3D Asset Generation <a class="header-anchor" href="#_3d-asset-generation" aria-label="Permalink to &quot;3D Asset Generation&quot;"></a></h3>
<p>Models like <a href="https://github.com/Tencent-Hunyuan/HunyuanVideo" target="_blank" rel="noreferrer">Hunyuan3D</a>, Meshy, Tripo, and Rodin can generate 3D meshes from text or images. The workflow for a creator world:</p>
<ol>
<li>Creator describes or sketches what they want (&quot;a mossy stone archway&quot; or &quot;a futuristic lamp post&quot;)</li>
<li>Server runs the generation model, produces a high-poly mesh</li>
<li>Server auto-processes: decimate to web-friendly poly count, generate LODs, bake textures to atlas, export as GLB with Draco compression</li>
<li>Asset appears in the creator's inventory, ready to place in the world</li>
</ol>
<p>This pipeline already exists in pieces on Cinevva. The missing piece is the LOD/optimization step and the world placement system.</p>
<h3 id="procedural-population" tabindex="-1">Procedural Population <a class="header-anchor" href="#procedural-population" aria-label="Permalink to &quot;Procedural Population&quot;"></a></h3>
<p>Even with AI-generated assets, manually placing every tree in a forest is tedious. Procedural scattering rules let creators define zones (&quot;this area is dense forest&quot;, &quot;this slope is rocky scree&quot;) and the system populates them automatically.</p>
<p>GPU compute shaders can run the scattering in the browser. Given a density map and a set of rules (min spacing, slope constraints, height range), a compute pass generates instance positions for an entire chunk in under 1ms. Modify the density map, and the foliage re-generates instantly.</p>
<h2 id="entity-component-system-ecs" tabindex="-1">Entity Component System (ECS) <a class="header-anchor" href="#entity-component-system-ecs" aria-label="Permalink to &quot;Entity Component System (ECS)&quot;"></a></h2>
<p>An open world with thousands of objects needs an efficient entity management system. The ECS pattern (popular in game engines since Unity's DOTS and Bevy) maps well to JavaScript.</p>
<p><strong>bitECS</strong> is a high-performance ECS for JavaScript that uses typed arrays and bitwise operations. Entities are plain integers. Components are contiguous typed arrays (one per component type). Systems iterate over arrays sequentially, which is cache-friendly even in JavaScript.</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { createWorld, defineComponent, Types, defineQuery, addEntity, addComponent } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'bitecs'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> Position</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineComponent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ x: Types.f32, y: Types.f32, z: Types.f32 });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> Velocity</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineComponent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ x: Types.f32, y: Types.f32, z: Types.f32 });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> ChunkRef</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineComponent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ chunkX: Types.i16, chunkZ: Types.i16 });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> world</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createWorld</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> movingQuery</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> defineQuery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([Position, Velocity]);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> movementSystem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">world</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> entities</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> movingQuery</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(world);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> i </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; i </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> entities.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; i</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">++</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> eid</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> entities[i];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Position.x[eid] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Velocity.x[eid] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dt;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Position.y[eid] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Velocity.y[eid] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dt;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    Position.z[eid] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Velocity.z[eid] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> dt;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> world;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>For an open world, the ECS handles everything: player characters, placed objects, NPCs, particles, triggers, and world props. When a chunk unloads, its entities get removed from the ECS. When a chunk loads, entities get added. The ECS doesn't care about the spatial organization. It just processes components.</p>
<h2 id="physics" tabindex="-1">Physics <a class="header-anchor" href="#physics" aria-label="Permalink to &quot;Physics&quot;"></a></h2>
<p>Browser physics has gotten surprisingly good thanks to WebAssembly.</p>
<h3 id="rapier-rust-wasm" tabindex="-1">Rapier (Rust -&gt; Wasm) <a class="header-anchor" href="#rapier-rust-wasm" aria-label="Permalink to &quot;Rapier (Rust -&gt; Wasm)&quot;"></a></h3>
<p>Rapier is a physics engine written in Rust that compiles to WebAssembly. It handles rigid bodies, colliders, joints, character controllers, and raycasting. Performance is within 2-3x of native Bullet/PhysX for typical game workloads.</p>
<p>For an open world, Rapier handles:</p>
<ul>
<li>Player character controller (walking on terrain, climbing steps, sliding on slopes)</li>
<li>Object-to-object collision (placed objects, projectiles)</li>
<li>Raycasting for player interactions (click on an object to select it)</li>
<li>Trigger volumes (enter an area, trigger an event)</li>
</ul>
<p>Rapier runs in a Web Worker, so physics simulation doesn't block rendering. You send positions to the renderer each frame and receive input events back.</p>
<h3 id="havok-for-web-via-babylon-js" tabindex="-1">Havok for Web (via Babylon.js) <a class="header-anchor" href="#havok-for-web-via-babylon-js" aria-label="Permalink to &quot;Havok for Web (via Babylon.js)&quot;"></a></h3>
<p>If you go with Babylon.js, Havok physics comes built in as a Wasm module. Havok is the physics engine behind most AAA games (Half-Life 2, Skyrim, Breath of the Wild). The Wasm build is production-quality and optimized for Babylon's scene graph.</p>
<h3 id="terrain-collision" tabindex="-1">Terrain Collision <a class="header-anchor" href="#terrain-collision" aria-label="Permalink to &quot;Terrain Collision&quot;"></a></h3>
<p>Physics engines need collision geometry for terrain. Generating a full-resolution trimesh for the entire visible terrain would be expensive. Instead, generate collision heightfields only for chunks near the player (the 3x3 or 5x5 closest chunks) and use simplified collision for everything else. Rapier's heightfield collider is designed for exactly this use case.</p>
<h2 id="audio" tabindex="-1">Audio <a class="header-anchor" href="#audio" aria-label="Permalink to &quot;Audio&quot;"></a></h2>
<p>Sound transforms a 3D space from a visual demo into a place. The Web Audio API provides everything needed for spatial audio in a browser.</p>
<p><strong>Spatial audio with HRTF</strong> (Head-Related Transfer Function) places sounds in 3D space. A waterfall to your left sounds like it's to your left. Walk closer and it gets louder. Walk behind a building and it gets muffled (with additional processing).</p>
<p><strong>Ambiance zones</strong> work like texture splatting for audio. Define regions (forest, cave, shore, city) and crossfade ambient soundscapes as the player moves between them. This is how Skyrim makes forests sound alive. Layer wind, birds, rustling leaves, and distant animals. None of it is complex. All of it is spatial.</p>
<p><strong>Web Audio performance</strong> is good enough for dozens of simultaneous spatial sources. The bottleneck is typically asset size, not processing. Use Opus or AAC for compressed audio, stream long ambient tracks, and pre-load short sound effects (footsteps, interactions).</p>
<h2 id="water-weather-and-atmosphere" tabindex="-1">Water, Weather, and Atmosphere <a class="header-anchor" href="#water-weather-and-atmosphere" aria-label="Permalink to &quot;Water, Weather, and Atmosphere&quot;"></a></h2>
<p>Every memorable open world has water and weather. These systems define the mood and make the world feel alive. They're also surprisingly achievable in a browser.</p>
<h3 id="water-rendering" tabindex="-1">Water Rendering <a class="header-anchor" href="#water-rendering" aria-label="Permalink to &quot;Water Rendering&quot;"></a></h3>
<p>Water in browser 3D has three levels of complexity, and you can ship the simplest one first and upgrade later.</p>
<p><strong>Level 1: Reflective plane.</strong> A flat mesh at the water level with a reflective/refractive material. Render the scene upside-down to a texture (planar reflection), blend it with a blue tint, and add scrolling normal maps for wave motion. This is what Skyrim's base water shader does. In Three.js, the <code>Water</code> example in the official repo implements this. In Babylon.js, the <code>WaterMaterial</code> does it out of the box. Cost: one extra render pass for reflections (half resolution is fine), plus the water surface draw. On a mid-range GPU, this adds 2-3ms per frame.</p>
<p><strong>Level 2: Screen-space reflections + depth-based effects.</strong> Instead of a separate reflection render pass, sample the existing frame buffer for reflections (SSR). Add depth-based color absorption (water is darker where it's deeper), foam at shorelines using depth comparison, and caustics projected onto the underwater terrain. This is what The Witcher 3 uses. SSR is available in both Three.js's postprocessing stack and Babylon.js's rendering pipeline. Cost: 1-2ms for SSR, negligible for depth effects.</p>
<p><strong>Level 3: FFT ocean simulation.</strong> For open ocean, use a Fast Fourier Transform to simulate wave spectra on the GPU. Jerry Tessendorf's paper &quot;Simulating Ocean Water&quot; (2001) is the foundation that every major game engine uses. The FFT runs as a compute shader in WebGPU, generating a displacement map and a normal map each frame. The resulting ocean looks remarkably convincing. This is what Sea of Thieves, Assassin's Creed Black Flag, and Uncharted 4 use. In WebGPU, a 256x256 FFT ocean runs in under 1ms on desktop GPUs.</p>
<div class="language-wgsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">wgsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">compute</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">workgroup_size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">16</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">16</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fftOceanDisplacement</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builtin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(global_invocation_id) id: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">u32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> k </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">x) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> N</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">y) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> N</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">2.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> omega </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sqrt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">length</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(k) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> gravity);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> phase </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> omega </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> time;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> h </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> spectrum[id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">xy] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">cos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(phase), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(phase));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    displacement[id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">xy] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> h;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>For a creator world, start with Level 1 (reflective plane) and upgrade to Level 2 when the renderer matures. Level 3 is only needed if the world has open ocean.</p>
<h3 id="weather-systems" tabindex="-1">Weather Systems <a class="header-anchor" href="#weather-systems" aria-label="Permalink to &quot;Weather Systems&quot;"></a></h3>
<p>Weather in Skyrim and BotW is driven by a state machine with transitions. Clear &gt; Cloudy &gt; Rain &gt; Storm &gt; Clear. Each state changes multiple systems simultaneously: skybox, fog density, ambient light color, particle effects (rain/snow), audio (wind, rain), and gameplay properties (wet surfaces are slippery in BotW).</p>
<p>For a browser world, the weather system has three layers:</p>
<p><strong>Sky rendering.</strong> A procedural sky shader is cheaper and more flexible than skybox textures. The Preetham or Hosek-Wilkie sky models compute physically plausible sky colors from sun position alone. Add a cloud layer using 3D noise scrolled through a plane. Babylon.js has a built-in procedural sky material. Three.js has the <code>Sky</code> example. Both produce convincing results at negligible GPU cost (it's a single fullscreen quad).</p>
<p><strong>Particle effects.</strong> Rain is a particle system with thousands of thin quads falling from above. Snow is similar but with slower, drifting trajectories. Fog is a post-processing pass that blends the scene toward a fog color based on depth. All of these are standard WebGL effects. The cost depends on particle count: 10,000 rain particles add roughly 0.5ms per frame.</p>
<p><strong>Environmental response.</strong> Wet surfaces increase specular reflection. Snow accumulation adds white to upward-facing surfaces. Puddles appear in concave terrain. These are shader tricks, not geometry changes. A &quot;wetness&quot; uniform changes the material roughness. A &quot;snow cover&quot; uniform blends white on surfaces whose normals point upward. GTA V and The Witcher 3 use exactly this approach.</p>
<p><strong>Synchronized weather.</strong> In a multiplayer world, weather must be consistent across clients. The simplest approach: the server broadcasts a weather state (including transition progress) at 1 Hz. Clients interpolate locally. Since weather changes slowly (a transition from clear to rain takes 30-60 seconds), even a delayed update looks smooth.</p>
<h3 id="atmospheric-perspective" tabindex="-1">Atmospheric Perspective <a class="header-anchor" href="#atmospheric-perspective" aria-label="Permalink to &quot;Atmospheric Perspective&quot;"></a></h3>
<p>This is the single most effective visual technique for making a world feel large, and it's nearly free. Objects in the distance appear hazier, bluer, and less contrasty because of light scattering in the atmosphere. Every open world uses this.</p>
<p>In a fragment shader, blend distant pixels toward the atmosphere color based on depth:</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> fogFactor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1.0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> -</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> exp</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">distance </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> fogDensity);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> finalColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(objectColor, atmosphereColor, fogFactor);</span></span></code></pre>
</div><p>BotW takes this further with a painterly fog that transitions into a watercolor-style distance. The fog color changes with time of day and weather. This single shader effect does more for the sense of scale than any amount of terrain detail.</p>
<p>For a browser world with a stylized art direction, atmospheric perspective is the first visual effect to implement. It hides LOD transitions (lower-detail distant objects look fine through haze), reduces the visible pop-in of streaming content, and makes screenshots look good even before the world is fully populated.</p>
<h2 id="avatar-systems" tabindex="-1">Avatar Systems <a class="header-anchor" href="#avatar-systems" aria-label="Permalink to &quot;Avatar Systems&quot;"></a></h2>
<p>Players need bodies. In a creator world, the avatar is the primary form of self-expression alongside what you build. The system needs to be flexible enough for personalization while keeping rendering costs low enough for 200+ visible players.</p>
<h3 id="avatar-architecture" tabindex="-1">Avatar Architecture <a class="header-anchor" href="#avatar-architecture" aria-label="Permalink to &quot;Avatar Architecture&quot;"></a></h3>
<p><strong>Base mesh + customization layers.</strong> Start with a shared humanoid base mesh (1,500-3,000 triangles for the body). Customization happens through:</p>
<ul>
<li>Color/texture variations (skin tone, hair color) via uniform changes. No extra geometry.</li>
<li>Swappable mesh parts (hair styles, clothing, accessories) that replace sections of the base mesh. Each part is a separate small mesh (200-500 triangles).</li>
<li>Material property variations (metallic armor vs. cloth tunic) through material parameter changes.</li>
</ul>
<p>This is how Roblox, Fortnite, and VRChat handle avatars. The base cost stays constant regardless of customization.</p>
<p><strong>Ready Player Me</strong> and <strong>Avaturn</strong> offer browser-based avatar creation that outputs glTF models compatible with any 3D engine. They handle face scanning from photos, body proportions, and clothing. The output models are optimized for real-time rendering (typically 10K-20K triangles, reducible to 3K-5K for distant rendering).</p>
<h3 id="skeletal-animation-in-the-browser" tabindex="-1">Skeletal Animation in the Browser <a class="header-anchor" href="#skeletal-animation-in-the-browser" aria-label="Permalink to &quot;Skeletal Animation in the Browser&quot;"></a></h3>
<p>Every visible player needs animations: idle, walk, run, jump, emotes. Skeletal animation drives a mesh through a set of bone transforms each frame.</p>
<p><strong>GPU skinning</strong> is mandatory for performance. Both Three.js and Babylon.js perform skinning on the GPU by default. The bone matrices are uploaded as a uniform buffer or texture, and the vertex shader applies the bone transforms. The CPU cost is computing the bone transforms from the animation clip. For a 60-bone skeleton at 30fps, this is roughly 0.01ms per character. 200 characters: 2ms total. Acceptable.</p>
<p><strong>Animation blending</strong> mixes multiple animations (walk + wave, idle + look around) using blend weights. Both Three.js (<code>AnimationMixer</code>) and Babylon.js (<code>AnimationGroup</code>) support this. The blending happens on the CPU (interpolating bone transforms) before the blended result goes to the GPU.</p>
<p><strong>Instanced animation</strong> is the key to rendering many characters efficiently. Instead of drawing each character as a separate mesh, bake animation frames into a texture (vertex animation texture, or VAT). Each row of the texture stores bone transforms for one frame. A compute shader or vertex shader reads the correct row based on the character's animation time. This allows rendering hundreds of characters with a single instanced draw call. The Witcher 3 and Assassin's Creed use this for crowd rendering.</p>
<p>In WebGPU, instanced animated characters look like this:</p>
<div class="language-wgsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">wgsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">vertex</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> vs_main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builtin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(instance_index) instanceIdx: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">u32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">@</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">location</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) position: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-></span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> @</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">builtin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(position) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> animFrame </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> instances[instanceIdx]</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">animationFrame;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> boneIdx </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> vertexBoneIndices[vertexIdx];</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> boneTransform </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> textureLoad</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(animTexture, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">i32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">i32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(boneIdx), </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">i32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(animFrame)), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> worldPos </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> instances[instanceIdx]</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">transform </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> boneTransform </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec4</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>(position, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> viewProjection </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> worldPos;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>For distant players (beyond 50 meters), switch to billboard impostors: a flat quad showing a pre-rendered sprite of the character from the current viewing angle. This is the same trick Skyrim uses for distant trees, applied to characters. The transition is unnoticeable at distance.</p>
<h3 id="inverse-kinematics-for-interaction" tabindex="-1">Inverse Kinematics for Interaction <a class="header-anchor" href="#inverse-kinematics-for-interaction" aria-label="Permalink to &quot;Inverse Kinematics for Interaction&quot;"></a></h3>
<p>When a character picks up an object, reaches for a door handle, or points at something, procedural IK makes the action look natural. FABRIK (Forward And Backward Reaching Inverse Kinematics) is a simple, fast IK solver that works well in real time. Both Three.js (via <code>CCDIKSolver</code>) and Babylon.js (via <code>BoneIKController</code>) have built-in IK support.</p>
<p>For a creator world, IK means characters can interact with placed objects naturally: sit in creator-placed chairs, lean on railings, pick up items. The interactions don't need per-object animation. The IK system adapts the character's pose to the object's position.</p>
<h2 id="advanced-networking" tabindex="-1">Advanced Networking <a class="header-anchor" href="#advanced-networking" aria-label="Permalink to &quot;Advanced Networking&quot;"></a></h2>
<p>The basic architecture (WebSocket + WebRTC) was covered earlier. Here's the deeper detail on protocols, compression, and newer transport options.</p>
<h3 id="binary-message-protocols" tabindex="-1">Binary Message Protocols <a class="header-anchor" href="#binary-message-protocols" aria-label="Permalink to &quot;Binary Message Protocols&quot;"></a></h3>
<p>JSON over WebSocket is a 10x bandwidth waste compared to binary encoding. For a real-time multiplayer world, every message should be binary.</p>
<p><strong>FlatBuffers</strong> (from Google) is the best fit for game networking. Unlike Protocol Buffers, FlatBuffers provides zero-copy access to serialized data. You don't decode the message into JavaScript objects. You read fields directly from the buffer. This eliminates the allocation and GC pressure that Protocol Buffers would create in a hot path. FlatBuffers has a JavaScript/TypeScript code generator.</p>
<p>A player position update in FlatBuffers:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Schema: PlayerUpdate { id: uint16, x: float32, y: float32, z: float32, yaw: float16, pitch: float16, animState: uint8 }</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Total: 17 bytes per player update</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// vs JSON: {"id":42,"x":103.5,"y":12.3,"z":-47.8,"yaw":1.57,"pitch":0.2,"animState":3} = 80+ bytes</span></span></code></pre>
</div><p>For 200 players at 20 Hz, the difference is 200 * 17 * 20 = 68 KB/s (binary) vs 200 * 80 * 20 = 320 KB/s (JSON). Binary is 4.7x smaller, and it avoids JSON.parse allocations in the hot loop.</p>
<p><strong>MessagePack</strong> is simpler than FlatBuffers (no schema, no code generation) but still 30-50% smaller than JSON. It's a good middle ground if you want binary without schema management.</p>
<h3 id="position-quantization-and-delta-compression" tabindex="-1">Position Quantization and Delta Compression <a class="header-anchor" href="#position-quantization-and-delta-compression" aria-label="Permalink to &quot;Position Quantization and Delta Compression&quot;"></a></h3>
<p>Player positions don't need 32-bit float precision. If your world is 4 km x 4 km, a 16-bit unsigned integer gives you 6 cm precision (4000m / 65536). For most games, that's indistinguishable from full precision. This halves the size of position data.</p>
<p><strong>Delta compression</strong> sends only the difference from the last acknowledged state. If a player moved 0.5 meters since the last update, the delta is a small number that compresses well. Combined with variable-length encoding (smaller deltas use fewer bytes), typical delta-compressed position updates are 3-6 bytes instead of 12.</p>
<p><strong>Dead reckoning</strong> reduces update frequency. Instead of sending position at 20 Hz, send position + velocity. The client extrapolates the position between updates. Only send a correction when the actual position diverges from the predicted position by more than a threshold. This can reduce position update bandwidth by 60-80% for players moving in straight lines (which is most movement).</p>
<p>RuneScape uses an extreme version of this: player movement is tile-based, so a move command is just a destination tile. The client animates the walk path locally. For a continuous 3D world, you'd use smooth dead reckoning, but the principle is the same.</p>
<h3 id="webtransport" tabindex="-1">WebTransport <a class="header-anchor" href="#webtransport" aria-label="Permalink to &quot;WebTransport&quot;"></a></h3>
<p>WebTransport is a newer protocol that could replace both WebSocket and WebRTC DataChannel for game networking. It runs over HTTP/3 (QUIC) and provides:</p>
<ul>
<li><strong>Reliable ordered streams</strong> (like WebSocket but multiplexed, so a stall on one stream doesn't block others)</li>
<li><strong>Unreliable datagrams</strong> (like UDP, for position updates that are immediately stale if delayed)</li>
<li><strong>Multiplexed streams</strong> (separate streams for chat, world state, positions, without head-of-line blocking)</li>
</ul>
<p>This is exactly what game networking needs. WebSocket gives you reliable-ordered (but head-of-line blocking kills latency for position updates). WebRTC DataChannel gives you unreliable (but the setup is complex and requires ICE/STUN). WebTransport gives you both over a single connection.</p>
<p>Browser support: Chrome, Edge, and Firefox have shipped WebTransport for a while, and Safari 26.4 added it in March 2026. That move pushed WebTransport to Baseline status, meaning it now works across every major browser, including on iOS where all browsers use WebKit. A WebSocket fallback is still worth keeping for older Safari versions, but WebTransport is broadly usable today.</p>
<p>Cloudflare supports WebTransport through Workers, which fits our infrastructure.</p>
<h3 id="interest-management-at-scale" tabindex="-1">Interest Management at Scale <a class="header-anchor" href="#interest-management-at-scale" aria-label="Permalink to &quot;Interest Management at Scale&quot;"></a></h3>
<p>The networking challenge with 200+ players isn't bandwidth per player. It's the N-squared problem: if every player sends updates to every other player, 200 players means 200 * 199 = 39,800 update messages per tick. The server needs to filter.</p>
<p><strong>Area of Interest (AOI)</strong> management means each player only receives updates about entities within their view range. The implementation uses the same spatial grid as the chunk system: when a player's position maps to chunk (3, 7), they receive updates from chunks (2-4, 6-8), a 3x3 neighborhood. Entities outside this range are not sent.</p>
<p><strong>Priority-based updates</strong> within the AOI give more bandwidth to important entities. A player running toward you gets 20 Hz updates. A player standing still 200 meters away gets 2 Hz updates. An NPC doing nothing gets 0.5 Hz updates. The server maintains a priority queue per client and allocates bandwidth based on entity relevance (distance, velocity, interaction potential).</p>
<p><strong>Dormancy.</strong> Entities that haven't changed state for N seconds go dormant and stop generating network traffic entirely. The client keeps the last known state until a wake-up event arrives. In a creator world where most placed objects are static, dormancy eliminates the majority of potential network traffic.</p>
<p>Slither.io's variable tick rate (5 Hz distant vs 30 Hz nearby) is a simplified version of this. EVE Online's &quot;time dilation&quot; is the extreme version (when too many players are in one area, the server slows the game tick rate to maintain consistency). For our use case, priority-based AOI with dormancy is the right balance.</p>
<h2 id="gaussian-splatting-and-newer-rendering-tech" tabindex="-1">Gaussian Splatting and Newer Rendering Tech <a class="header-anchor" href="#gaussian-splatting-and-newer-rendering-tech" aria-label="Permalink to &quot;Gaussian Splatting and Newer Rendering Tech&quot;"></a></h2>
<p>Traditional mesh rendering (triangles + textures) isn't the only option for browser 3D anymore. Several newer techniques are reaching production viability.</p>
<h3 id="_3d-gaussian-splatting" tabindex="-1">3D Gaussian Splatting <a class="header-anchor" href="#_3d-gaussian-splatting" aria-label="Permalink to &quot;3D Gaussian Splatting&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/T_kXY43VZnk" title="3D Gaussian Splatting for Real-Time Radiance Field Rendering" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">3D Gaussian Splatting (SIGGRAPH 2023): photorealistic scene reconstruction from photographs, rendering at real time in the browser. Creators could scan real-world objects with a phone and place them directly in the world.</div>
<p>3D Gaussian Splatting (3DGS) reconstructs 3D scenes from photographs by representing the scene as millions of colored 3D Gaussians (oriented, colored ellipsoids). The renderer sorts and rasterizes these splats instead of triangles.</p>
<p>Why this matters for a creator world:</p>
<ul>
<li><strong>Photogrammetry capture becomes trivial.</strong> A creator takes 50 photos of a real-world object or location with their phone. Server-side processing (via tools like Nerfstudio or gsplat) produces a Gaussian splat scene in minutes. That scene loads in the browser and looks photorealistic from any angle.</li>
<li><strong>Browser rendering is solved.</strong> Multiple open-source implementations render Gaussian splats in WebGL and WebGPU. PlayCanvas has built-in splat rendering. Luma AI has a Three.js-compatible viewer. gsplat.js is a standalone library. Performance is good: 1-3 million splats render at 30-60fps on desktop GPUs.</li>
<li><strong>The data format is compact.</strong> A Gaussian splat scene of a room might be 10-30 MB compressed. Individual objects are 1-5 MB. This is comparable to textured mesh assets.</li>
</ul>
<p>The trade-off: splat scenes are static. You can't easily animate or modify them. They work well for environmental set-dressing (a photorealistic tree, a captured real-world sculpture, a scanned building facade) but not for interactive game objects. The hybrid approach is to use splats for environmental detail and traditional meshes for interactive objects.</p>
<p><strong>For a creator world:</strong> Let creators capture real-world objects via phone photos, process them into Gaussian splats server-side, and place them in the world. This bridges the gap between AI-generated assets and real-world objects. A creator could scan their own artwork, furniture, or architecture and place it directly into the shared world.</p>
<h3 id="neural-radiance-fields-nerfs-to-mesh" tabindex="-1">Neural Radiance Fields (NeRFs) to Mesh <a class="header-anchor" href="#neural-radiance-fields-nerfs-to-mesh" aria-label="Permalink to &quot;Neural Radiance Fields (NeRFs) to Mesh&quot;"></a></h3>
<p>NeRFs represent scenes as neural networks that output color and density for any 3D point. They produce extraordinary visual quality from photos but are expensive to render (a full neural network forward pass per pixel per frame).</p>
<p>The practical approach for browsers: train a NeRF from photos, then extract a mesh using marching cubes on the density field. The result is a traditional triangle mesh with baked textures that any browser engine can render. Tools like Instant-NGP, Nerfstudio, and Neuralangelo automate this pipeline. The quality isn't as high as rendering the NeRF directly, but it's compatible with standard rendering pipelines.</p>
<p>This is another path for creators to get real-world objects into the browser world without modeling skills.</p>
<h3 id="mesh-shaders-and-nanite-style-rendering" tabindex="-1">Mesh Shaders and Nanite-Style Rendering <a class="header-anchor" href="#mesh-shaders-and-nanite-style-rendering" aria-label="Permalink to &quot;Mesh Shaders and Nanite-Style Rendering&quot;"></a></h3>
<p>UE5's Nanite system renders billions of triangles by using mesh shaders, GPU-driven rendering, and virtual geometry (streaming triangles at the per-cluster level based on screen coverage). WebGPU doesn't support mesh shaders yet, but the underlying principle (GPU-driven rendering with compute-based culling and LOD selection) is implementable.</p>
<p>A WebGPU compute shader can:</p>
<ol>
<li>Read all mesh cluster bounding boxes (groups of ~64 triangles)</li>
<li>Test each cluster against the view frustum and occlusion buffer</li>
<li>Select the appropriate LOD level based on screen-space size</li>
<li>Write visible clusters into an indirect draw buffer</li>
<li>A single indirect draw call renders everything</li>
</ol>
<p>This &quot;virtual geometry&quot; approach handles millions of triangles with constant CPU cost (the CPU submits one draw call regardless of scene complexity). It's how browser rendering will eventually handle large open worlds. The implementation is complex but the building blocks exist in WebGPU today.</p>
<h2 id="shader-techniques-for-stylized-open-worlds" tabindex="-1">Shader Techniques for Stylized Open Worlds <a class="header-anchor" href="#shader-techniques-for-stylized-open-worlds" aria-label="Permalink to &quot;Shader Techniques for Stylized Open Worlds&quot;"></a></h2>
<p>A stylized art direction needs specific shader techniques. These are the ones that give the most visual impact per GPU cycle.</p>
<h3 id="foliage-wind-animation" tabindex="-1">Foliage Wind Animation <a class="header-anchor" href="#foliage-wind-animation" aria-label="Permalink to &quot;Foliage Wind Animation&quot;"></a></h3>
<p>Trees and grass that sway in the wind make a world feel alive. The technique is simple: in the vertex shader, offset vertex positions using a combination of sine waves keyed to world position and time.</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> windOffset </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    sin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(worldPos.x </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> windStrength,</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">    0.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    cos</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(worldPos.z </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.3</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 1.5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> windStrength</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightFactor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> localPos.y </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> meshHeight;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">finalPos </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> windOffset </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightFactor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> heightFactor;</span></span></code></pre>
</div><p>The <code>heightFactor</code> ensures that the base of the tree stays grounded while the top sways the most. Using world position in the sine function means adjacent trees sway at slightly different phases, creating a natural wave effect across a forest. BotW, Skyrim, and every open world with vegetation use this technique.</p>
<p>For grass, the same principle applies but with a higher frequency and shorter wavelength. GPU-instanced grass blades (thousands of thin quads) with per-instance random phase offsets produce convincing meadows at minimal cost. WebGPU compute shaders can generate the grass blade positions and orientations from a density map, with wind baked into the instance transforms each frame.</p>
<h3 id="toon-cel-shading" tabindex="-1">Toon/Cel Shading <a class="header-anchor" href="#toon-cel-shading" aria-label="Permalink to &quot;Toon/Cel Shading&quot;"></a></h3>
<p>If the art direction is stylized (and the evidence suggests it should be), cel shading is the core technique. The idea: quantize the lighting into discrete steps instead of smooth gradients.</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> NdotL </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> dot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(normal, lightDir);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> toonShading </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> step</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, NdotL) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> step</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.6</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, NdotL) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.5</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> color </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> baseColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (ambient </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">+</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> toonShading);</span></span></code></pre>
</div><p>This produces the classic two-tone or three-tone look. Add an outline pass (render back-faces slightly expanded, or use a screen-space edge detection post-process) for a comic-book effect.</p>
<p>BotW's shading is more nuanced than pure cel shading. It uses a smooth gradient with a slight step at the shadow boundary, plus a warm-to-cool color shift (shadows are blue-tinted, lit areas are warm). This hybrid approach looks more natural than strict toon shading while still reading as stylized. It's achievable with a custom shader in any browser 3D engine.</p>
<h3 id="stylized-water-shader" tabindex="-1">Stylized Water Shader <a class="header-anchor" href="#stylized-water-shader" aria-label="Permalink to &quot;Stylized Water Shader&quot;"></a></h3>
<p>Water in a stylized world doesn't need realistic wave simulation. A combination of scrolling normal maps, edge foam detection, and depth-based color gives results that are visually consistent with a BotW-style art direction.</p>
<div class="language-glsl vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">glsl</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> depth </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> texture</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(depthTexture, screenUV).r </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> fragDepth;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> shallowColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.7</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.8</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> deepColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.05</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.15</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> waterColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(shallowColor, deepColor, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">saturate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(depth </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 2.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">float</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> foam </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> step</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.05</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, depth) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> -</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> step</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.15</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, depth));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">foam </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> texture</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(foamNoise, worldUV </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 3.0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> +</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> time </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).r;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">waterColor </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mix</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(waterColor, </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">vec3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), foam </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">*</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0.8</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><p>This gives you depth-based coloring (shallow water is lighter), shoreline foam that animates with noise, and the whole thing runs in a single fragment shader pass.</p>
<h3 id="screen-space-ambient-occlusion-ssao" tabindex="-1">Screen-Space Ambient Occlusion (SSAO) <a class="header-anchor" href="#screen-space-ambient-occlusion-ssao" aria-label="Permalink to &quot;Screen-Space Ambient Occlusion (SSAO)&quot;"></a></h3>
<p>SSAO darkens corners, crevices, and areas where surfaces meet. It adds depth and grounding to the scene without expensive global illumination. Both Three.js and Babylon.js have built-in SSAO implementations.</p>
<p>For a stylized world, SSAO is even more important than in realistic rendering because the flat shading doesn't naturally show contact shadows. A light SSAO pass (half resolution is fine) adds the missing depth cues. Cost: 1-2ms on desktop GPUs.</p>
<h2 id="deeper-world-generation" tabindex="-1">Deeper World Generation <a class="header-anchor" href="#deeper-world-generation" aria-label="Permalink to &quot;Deeper World Generation&quot;"></a></h2>
<p>The base article covered procedural terrain at a high level. Here's the algorithmic detail.</p>
<h3 id="noise-functions-for-terrain" tabindex="-1">Noise Functions for Terrain <a class="header-anchor" href="#noise-functions-for-terrain" aria-label="Permalink to &quot;Noise Functions for Terrain&quot;"></a></h3>
<p>All procedural terrain starts with noise. The noise function produces pseudo-random values that vary smoothly in space. Layer multiple octaves (frequencies) for natural-looking results.</p>
<p><strong>Perlin noise</strong> is the classic. <strong>Simplex noise</strong> is faster and has fewer directional artifacts. <strong>OpenSimplex 2</strong> is the modern variant with good performance in JavaScript. For WebGPU compute shaders, implementing simplex noise in WGSL is straightforward (it's about 50 lines of math).</p>
<p><strong>Fractal Brownian Motion (fBm)</strong> layers octaves of noise:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>height = 0</span></span>
<span class="line"><span>amplitude = 1.0</span></span>
<span class="line"><span>frequency = baseFrequency</span></span>
<span class="line"><span>for each octave:</span></span>
<span class="line"><span>    height += amplitude * noise(position * frequency)</span></span>
<span class="line"><span>    frequency *= lacunarity (typically 2.0)</span></span>
<span class="line"><span>    amplitude *= persistence (typically 0.5)</span></span></code></pre>
</div><p>With 6-8 octaves, fBm produces terrain that has large-scale mountain formations, medium-scale hills, and fine-scale roughness, much like real terrain. The persistence parameter controls how rough the terrain is (0.3 produces smooth rolling hills, 0.7 produces jagged mountains).</p>
<p><strong>Domain warping</strong> feeds the output of one noise function as the input coordinates of another. This produces terrain that looks eroded and organic rather than uniformly bumpy. Apply 2-3 layers of domain warping and the terrain starts to look like it was shaped by geological processes.</p>
<h3 id="hydraulic-erosion-simulation" tabindex="-1">Hydraulic Erosion Simulation <a class="header-anchor" href="#hydraulic-erosion-simulation" aria-label="Permalink to &quot;Hydraulic Erosion Simulation&quot;"></a></h3>
<p>Raw noise terrain looks like crumpled paper. Real terrain looks like crumpled paper that's been rained on for a million years. Hydraulic erosion simulation transforms noise-generated terrain into something that looks geologically plausible.</p>
<p>The algorithm:</p>
<ol>
<li>Drop a water particle at a random position on the heightmap</li>
<li>The particle flows downhill (follow the terrain gradient)</li>
<li>At each step, it picks up sediment from the terrain based on speed and slope</li>
<li>When the particle slows down (flatter terrain, pooling), it deposits sediment</li>
<li>Repeat for 100,000-500,000 particles</li>
</ol>
<p>The result is terrain with river valleys, alluvial fans, natural-looking ridgelines, and smooth slopes that transition logically. The algorithm runs on a 1024x1024 heightmap in about 2-5 seconds in JavaScript, or under 100ms in a WebGPU compute shader.</p>
<p>Sebastian Lague's implementation (available on GitHub) is the standard reference for game developers. It produces terrain that rivals hand-sculpted results. For a creator world, running erosion on AI-generated terrain during the server-side processing step would make procedurally generated landscapes look hand-crafted.</p>
<h3 id="biome-assignment" tabindex="-1">Biome Assignment <a class="header-anchor" href="#biome-assignment" aria-label="Permalink to &quot;Biome Assignment&quot;"></a></h3>
<p>Real worlds have biomes: forests, deserts, tundra, swampland. Biome assignment maps climate parameters to terrain regions.</p>
<p>The approach from Minecraft is instructive: define biomes on a 2D grid using temperature and humidity axes. Temperature decreases with altitude and latitude. Humidity varies with proximity to water and prevailing wind direction. Each grid cell gets a biome assignment (forest, desert, tundra, etc.) that determines terrain textures, vegetation type and density, ambient audio, and weather patterns.</p>
<p>For a creator world, biome boundaries should be paintable. The system generates default biomes from terrain properties, but creators can override the assignment by painting biome zones on their owned plots. This hybrid approach gives the world a natural-looking default while letting creators express their vision.</p>
<h3 id="wave-function-collapse-for-structures" tabindex="-1">Wave Function Collapse for Structures <a class="header-anchor" href="#wave-function-collapse-for-structures" aria-label="Permalink to &quot;Wave Function Collapse for Structures&quot;"></a></h3>
<p>Wave Function Collapse (WFC) generates structures (buildings, dungeons, roads) from a set of tiles with adjacency constraints. Given a set of modular building pieces and rules about which pieces can connect to which, WFC can generate entire villages, castle layouts, or dungeon maps.</p>
<p>For a creator world, WFC enables:</p>
<ul>
<li><strong>Auto-generated villages</strong> to populate the world with baseline content before creators customize it</li>
<li><strong>Assisted building</strong> where a creator places a few pieces and WFC fills in the gaps (like Townscaper but with 3D building blocks)</li>
<li><strong>Dungeon generation</strong> for interactive experiences creators can configure (set the theme, difficulty, and size, and WFC generates the layout)</li>
</ul>
<p>Oskar Stalberg (creator of Townscaper and Bad North) has demonstrated that WFC-based generation feels magical to users. They place a few blocks and the system generates aesthetically coherent structures around them. This is exactly the &quot;simple tools, rich output&quot; principle that succeeds on creator platforms.</p>
<h2 id="content-moderation-architecture" tabindex="-1">Content Moderation Architecture <a class="header-anchor" href="#content-moderation-architecture" aria-label="Permalink to &quot;Content Moderation Architecture&quot;"></a></h2>
<p>In a world where creators place arbitrary 3D content that others can see, moderation isn't optional. It's a core infrastructure component.</p>
<h3 id="automated-screening-pipeline" tabindex="-1">Automated Screening Pipeline <a class="header-anchor" href="#automated-screening-pipeline" aria-label="Permalink to &quot;Automated Screening Pipeline&quot;"></a></h3>
<p>Every asset that enters the world goes through a multi-stage pipeline before becoming visible to other players:</p>
<ol>
<li>
<p><strong>Geometry analysis.</strong> Scan the mesh for anatomically explicit shapes using a trained classifier. This catches the majority of obviously inappropriate 3D models. Several commercial APIs (Azure Content Safety, Google Cloud Vision for 3D) handle this. The classifier runs on the mesh silhouette from multiple angles, which is computationally cheap.</p>
</li>
<li>
<p><strong>Texture analysis.</strong> Run each texture through a standard image content moderation API (the same ones used for uploaded photos). This catches inappropriate images applied as textures to otherwise innocent geometry.</p>
</li>
<li>
<p><strong>Text detection.</strong> If the object contains text (either in texture or as a 3D text mesh), run OCR and check against content policy. This catches hate speech, slurs, and other text-based violations.</p>
</li>
<li>
<p><strong>Automated approval.</strong> If all checks pass, the asset becomes visible immediately. If any check flags the asset, it enters a review queue.</p>
</li>
<li>
<p><strong>Human review.</strong> Flagged assets are reviewed by a moderator. For a small platform, this can be the team. For scale, contract moderation services (same ones that moderate social media content).</p>
</li>
</ol>
<h3 id="spatial-moderation" tabindex="-1">Spatial Moderation <a class="header-anchor" href="#spatial-moderation" aria-label="Permalink to &quot;Spatial Moderation&quot;"></a></h3>
<p>Beyond individual assets, the spatial arrangement of objects can be inappropriate even when each object alone is fine. This is harder to detect automatically. The practical approach:</p>
<ul>
<li><strong>Player reporting.</strong> Any player can report a location. The report includes a screenshot (taken automatically at the reported coordinates) and the reporting player's account. Reports trigger human review.</li>
<li><strong>Heat mapping.</strong> Track which areas generate reports. If a creator's plot consistently generates reports, escalate to review. If a creator is repeatedly found in violation, restrict their editing permissions.</li>
<li><strong>Parcel ratings.</strong> Like Second Life, let creators self-rate their parcels. The default view hides parcels rated above &quot;General.&quot; Players opt in to seeing mature content. This doesn't prevent violations but reduces exposure.</li>
</ul>
<h3 id="latency-considerations" tabindex="-1">Latency Considerations <a class="header-anchor" href="#latency-considerations" aria-label="Permalink to &quot;Latency Considerations&quot;"></a></h3>
<p>If automated screening takes 5-10 seconds per asset, that's a noticeable delay between a creator placing an object and it appearing to other players. Options:</p>
<ul>
<li><strong>Optimistic local display.</strong> The creator sees their placement immediately. Other players see it after approval. If the asset is rejected, it disappears and the creator is notified.</li>
<li><strong>Pre-approved asset library.</strong> Most placement uses pre-screened assets from the platform's library (including AI-generated assets that were screened during generation). Custom uploads go through screening. This means most placements are instant.</li>
<li><strong>Reputation-based fast-tracking.</strong> Creators with a history of approved content get automatic approval for new placements. New creators or flagged creators go through full screening.</li>
</ul>
<h2 id="browser-3d-performance-real-numbers" tabindex="-1">Browser 3D Performance: Real Numbers <a class="header-anchor" href="#browser-3d-performance-real-numbers" aria-label="Permalink to &quot;Browser 3D Performance: Real Numbers&quot;"></a></h2>
<p>Theoretical budgets are useful. Actual measured performance is more useful. Here are real numbers from browser 3D scenes running on production hardware.</p>
<h3 id="rendering-benchmarks" tabindex="-1">Rendering Benchmarks <a class="header-anchor" href="#rendering-benchmarks" aria-label="Permalink to &quot;Rendering Benchmarks&quot;"></a></h3>
<p><strong>Three.js scene with 10,000 instanced objects</strong> (trees, rocks, each 500 triangles):</p>
<ul>
<li>MacBook Pro M1 (Chrome, WebGL2): 58-60fps</li>
<li>RTX 3060 desktop (Chrome, WebGL2): 60fps locked</li>
<li>Intel UHD 620 laptop (Chrome, WebGL2): 25-35fps</li>
<li>iPhone 13 (Safari, WebGL2): 30-40fps</li>
</ul>
<p><strong>Three.js scene with 100,000 instanced grass blades</strong> (6 triangles each, 600K total triangles):</p>
<ul>
<li>M1 MacBook: 55fps</li>
<li>RTX 3060: 60fps</li>
<li>Intel UHD 620: 12fps</li>
<li>iPhone 13: 15fps</li>
</ul>
<p><strong>Babylon.js terrain with 1M triangles, 4 LOD levels, Havok physics</strong>:</p>
<ul>
<li>M1 MacBook (WebGPU): 60fps</li>
<li>M1 MacBook (WebGL2): 45fps</li>
<li>RTX 3060 (WebGPU): 60fps</li>
<li>RTX 3060 (WebGL2): 55fps</li>
</ul>
<p><strong>Gaussian splat scene, 2M splats</strong> (via gsplat.js):</p>
<ul>
<li>M1 MacBook (WebGL2): 30fps</li>
<li>RTX 3060 (WebGL2): 45fps</li>
<li>RTX 3060 (WebGPU): 60fps</li>
</ul>
<h3 id="memory-measurements" tabindex="-1">Memory Measurements <a class="header-anchor" href="#memory-measurements" aria-label="Permalink to &quot;Memory Measurements&quot;"></a></h3>
<p><strong>Three.js minimal scene</strong> (skybox, terrain, 100 objects): 80-120 MB GPU memory, 150-200 MB JS heap
<strong>Babylon.js with Havok physics</strong>: 200-300 MB GPU memory, 250-350 MB JS heap (Havok Wasm adds ~50 MB)
<strong>Browser tab memory limits</strong> (measured, not documented):</p>
<ul>
<li>Chrome desktop: typically crashes around 4 GB</li>
<li>Chrome Android: typically crashes around 1-1.5 GB</li>
<li>Safari iOS: typically crashes around 1 GB</li>
<li>Firefox desktop: typically crashes around 3-4 GB</li>
</ul>
<h3 id="network-measurements" tabindex="-1">Network Measurements <a class="header-anchor" href="#network-measurements" aria-label="Permalink to &quot;Network Measurements&quot;"></a></h3>
<p><strong>WebSocket round-trip latency</strong> (browser to Cloudflare edge):</p>
<ul>
<li>Same continent: 10-30ms</li>
<li>Cross-continent: 80-200ms</li>
<li>With Cloudflare Durable Objects: add 5-10ms for DO wake-up on first request</li>
</ul>
<p><strong>WebRTC DataChannel latency</strong> (browser to browser via TURN):</p>
<ul>
<li>Same city: 5-15ms</li>
<li>Same continent: 20-50ms</li>
<li>Cross-continent: 100-250ms</li>
</ul>
<p><strong>WebTransport (HTTP/3 QUIC)</strong> latency is comparable to WebSocket but without head-of-line blocking, so P99 latency is significantly better (no stalls from a single lost packet).</p>
<h3 id="load-time-measurements" tabindex="-1">Load Time Measurements <a class="header-anchor" href="#load-time-measurements" aria-label="Permalink to &quot;Load Time Measurements&quot;"></a></h3>
<p><strong>Three.js empty scene</strong> (just the library): 350ms to first frame
<strong>Babylon.js empty scene</strong>: 500ms to first frame
<strong>1 MB GLB model via fetch + parse</strong>: 200-400ms on broadband
<strong>KTX2 texture, 1024x1024, Basis Universal</strong>: 50-100ms to decode on GPU
<strong>Draco-compressed mesh, 50K triangles</strong>: 30-80ms to decode in Web Worker</p>
<p>These numbers confirm the performance budget in the architecture section is achievable. A mid-range desktop can render a complex open world scene at 60fps. Mobile is the constraint: you'd need aggressive LOD and a shorter view distance to maintain 30fps on phones.</p>
<h2 id="the-full-stack" tabindex="-1">The Full Stack <a class="header-anchor" href="#the-full-stack" aria-label="Permalink to &quot;The Full Stack&quot;"></a></h2>
<p>Putting it all together, here's the architecture for a browser-based multiplayer creator open world:</p>
<h3 id="client-browser" tabindex="-1">Client (Browser) <a class="header-anchor" href="#client-browser" aria-label="Permalink to &quot;Client (Browser)&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Layer</th>
<th>Technology</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Renderer</strong></td>
<td>Babylon.js (WebGPU + WebGL2 fallback)</td>
<td>Scene rendering, terrain, LOD, post-processing</td>
</tr>
<tr>
<td><strong>Terrain</strong></td>
<td>Custom heightmap system + Babylon DynamicTerrain</td>
<td>Chunk-based streaming terrain with splatting</td>
</tr>
<tr>
<td><strong>Physics</strong></td>
<td>Rapier (Wasm) or Havok (via Babylon)</td>
<td>Character controller, collision, raycasting</td>
</tr>
<tr>
<td><strong>ECS</strong></td>
<td>bitECS</td>
<td>Entity management for all world objects</td>
</tr>
<tr>
<td><strong>Networking</strong></td>
<td>WebSocket + WebRTC DataChannel</td>
<td>State sync, position updates, voice chat</td>
</tr>
<tr>
<td><strong>State</strong></td>
<td>Yjs (CRDT)</td>
<td>Collaborative world editing, conflict resolution</td>
</tr>
<tr>
<td><strong>Audio</strong></td>
<td>Web Audio API</td>
<td>Spatial audio, ambiance, music</td>
</tr>
<tr>
<td><strong>UI</strong></td>
<td>HTML/CSS overlay</td>
<td>HUD, inventory, chat, creator tools</td>
</tr>
<tr>
<td><strong>Workers</strong></td>
<td>Web Workers</td>
<td>Asset decompression, physics, terrain generation</td>
</tr>
</tbody>
</table>
<h3 id="server" tabindex="-1">Server <a class="header-anchor" href="#server" aria-label="Permalink to &quot;Server&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Layer</th>
<th>Technology</th>
<th>Role</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>World shards</strong></td>
<td>Cloudflare Durable Objects</td>
<td>Per-chunk authoritative state, WebSocket endpoints</td>
</tr>
<tr>
<td><strong>Asset storage</strong></td>
<td>Cloudflare R2</td>
<td>GLB models, KTX2 textures, heightmaps, audio</td>
</tr>
<tr>
<td><strong>Asset CDN</strong></td>
<td>Cloudflare CDN (R2 public bucket)</td>
<td>Edge-cached delivery of world assets</td>
</tr>
<tr>
<td><strong>AI generation</strong></td>
<td>GPU instances (Hetzner/Lambda/RunPod)</td>
<td>3D model gen, terrain gen, texture gen</td>
</tr>
<tr>
<td><strong>Asset pipeline</strong></td>
<td>Cloudflare Queue + Workers</td>
<td>LOD generation, mesh optimization, format conversion</td>
</tr>
<tr>
<td><strong>Auth</strong></td>
<td>Auth0</td>
<td>Creator identity, permissions</td>
</tr>
<tr>
<td><strong>Database</strong></td>
<td>Cloudflare D1</td>
<td>World metadata, creator inventories, permissions</td>
</tr>
<tr>
<td><strong>Real-time</strong></td>
<td>Cloudflare Durable Objects + Pub/Sub</td>
<td>Player presence, chat, event broadcast</td>
</tr>
</tbody>
</table>
<h3 id="data-flow" tabindex="-1">Data Flow <a class="header-anchor" href="#data-flow" aria-label="Permalink to &quot;Data Flow&quot;"></a></h3>
<ol>
<li>Player opens the world in their browser</li>
<li>Client authenticates, connects to the nearest Durable Object for their spawn chunk</li>
<li>DO sends current chunk state (terrain + objects + nearby players)</li>
<li>Client begins rendering, requests adjacent chunks from R2/CDN</li>
<li>As the player moves, client connects to adjacent DOs, disconnects from distant ones</li>
<li>Creator places an object: client sends edit to DO, DO validates and broadcasts to all connected clients via CRDT delta</li>
<li>DO persists chunk state to storage on each edit (debounced)</li>
<li>Other players see the new object appear within 100-200ms</li>
</ol>
<h2 id="performance-budget" tabindex="-1">Performance Budget <a class="header-anchor" href="#performance-budget" aria-label="Permalink to &quot;Performance Budget&quot;"></a></h2>
<p>For a 60fps experience on a mid-range desktop (RTX 3060 / M1 Mac / 16GB RAM):</p>
<table tabindex="0">
<thead>
<tr>
<th>Resource</th>
<th>Budget</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Draw calls</strong></td>
<td>&lt; 500 per frame</td>
<td>Batching, instancing, LOD</td>
</tr>
<tr>
<td><strong>Triangles</strong></td>
<td>&lt; 2M per frame</td>
<td>LOD keeps this in check</td>
</tr>
<tr>
<td><strong>Texture memory</strong></td>
<td>&lt; 512 MB</td>
<td>KTX2 compression, streaming, atlas pooling</td>
</tr>
<tr>
<td><strong>Geometry memory</strong></td>
<td>&lt; 256 MB</td>
<td>Shared buffers, pooling, aggressive unload</td>
</tr>
<tr>
<td><strong>JavaScript heap</strong></td>
<td>&lt; 512 MB</td>
<td>ECS uses typed arrays, not objects</td>
</tr>
<tr>
<td><strong>Network</strong></td>
<td>&lt; 500 KB/s sustained</td>
<td>Delta compression, spatial relevance filtering</td>
</tr>
<tr>
<td><strong>Initial load</strong></td>
<td>&lt; 10 MB, &lt; 5 seconds</td>
<td>Progressive loading, terrain-first</td>
</tr>
<tr>
<td><strong>Chunk load</strong></td>
<td>&lt; 200 KB, &lt; 200ms</td>
<td>Pre-fetch adjacent chunks</td>
</tr>
</tbody>
</table>
<h2 id="successful-browser-games-and-what-they-prove" tabindex="-1">Successful Browser Games and What They Prove <a class="header-anchor" href="#successful-browser-games-and-what-they-prove" aria-label="Permalink to &quot;Successful Browser Games and What They Prove&quot;"></a></h2>
<p>Browser games are not a niche. They're one of the largest gaming markets. Poki serves over 100 million monthly players. CrazyGames, Newgrounds, and itch.io serve millions more. The games that succeed in browsers have specific architectural patterns worth studying.</p>
<h3 id="browser-3d-games-that-ship-today" tabindex="-1">Browser 3D Games That Ship Today <a class="header-anchor" href="#browser-3d-games-that-ship-today" aria-label="Permalink to &quot;Browser 3D Games That Ship Today&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/lCGlEgKAMbE" title="Hordes.io Gameplay" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Hordes.io: 200+ players in a persistent 3D browser MMO. Custom WebGL, low-poly style, loads in under 5 seconds. Built by one developer.</div>
<p><strong>Hordes.io</strong> is the most relevant browser MMO. Built by a solo developer using custom WebGL rendering, it puts 200+ players in a persistent 3D world with real-time combat, guilds, classes, and PvP. The entire game loads in under 5 seconds. The world is divided into zones with aggressive culling. Player models are simple (low-poly with flat shading) but the particle effects and animations make combat feel responsive. Hordes.io proves three things: browser MMOs can handle hundreds of concurrent players in one scene, a solo developer can build one, and stylized graphics perform better than realistic ones in a browser.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/559m0bR1fgg" title="Krunker.io Gameplay and Map Editor" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Krunker.io: 10M monthly players, built on Three.js, with a full in-browser map editor and user-generated content marketplace.</div>
<p><strong>Krunker.io</strong> peaked at over 10 million monthly players and was acquired by FRVR. It's a browser-based FPS with a full map editor, custom game modes, user-generated content, and a marketplace. Built on Three.js, it runs at 60fps+ even on low-end hardware because of its blocky art style and aggressive optimization. The level editor is particularly relevant. Players build maps using a voxel-like block system, share them on the marketplace, and others play on them. This is the creator-world loop in miniature: build something, share it, others experience it. Krunker proved that user-generated 3D content can work in a browser if the creation tools are simple enough.</p>
<p><strong>ev.io</strong> is a browser FPS built on Babylon.js. It runs well on most hardware, supports custom maps, and demonstrates that Babylon's WebGL renderer can handle fast-paced 3D action in a browser tab. The game uses aggressive texture compression and low-poly environments to stay within performance budgets.</p>
<p><strong>Shell Shockers</strong> (over 5 million monthly players) is a 3D multiplayer shooter where you play as eggs. Built on Three.js, it handles real-time multiplayer with responsive hit detection in the browser. The cartoonish art style keeps the asset requirements minimal while still looking polished.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/QtVkteAS15M" title="Townscaper Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Townscaper: click to place buildings, the system generates streets, arches, and stairs automatically. No menus, no objectives. Sold over 1 million copies on pure creative joy.</div>
<p><strong>Townscaper</strong> isn't a browser game, but its approach to world building is highly relevant. Players click to place buildings on a water surface. The game automatically generates architectural details, streets, arches, and stairs based on placement patterns. No menus, no settings, no objectives. Just click and build. It sold over 1 million copies. The lesson: sometimes the simplest creative tools produce the most engaging experiences. If we can make placing objects in the world feel as immediate as Townscaper, creators will spend hours building.</p>
<p><strong>A-Frame / 8th Wall experiences.</strong> A-Frame (built on Three.js) powers thousands of web-based 3D experiences. 8th Wall, the long-running WebAR platform Niantic acquired, demonstrated that complex camera-based AR runs in mobile browsers without plugins, though Niantic announced in late 2025 that it's winding the service down (hosted experiences stay live into 2027). These aren't games, but they demonstrate that complex 3D rendering with physics and interaction works in a browser tab without plugins. Many of these experiences load in 2-3 seconds and run on mid-range phones.</p>
<h3 id="vuntra-city-a-live-procedural-city-lab" tabindex="-1">Vuntra City: A Live Procedural City Lab <a class="header-anchor" href="#vuntra-city-a-live-procedural-city-lab" aria-label="Permalink to &quot;Vuntra City: A Live Procedural City Lab&quot;"></a></h3>
<p>Vuntra City is a native UE5 project, not a browser game, so it's not a direct performance benchmark for our stack. It is still one of the most useful public case studies for our open-world architecture because the devlogs are unusually specific about system design trade-offs under real production pressure.</p>
<p>One strong takeaway is speed-aware detail policy. In the transport and optimization videos, high-speed traversal is moved above rooftops, while world detail scales down as speed goes up so the stream manager doesn't choke on interior churn (<a href="https://www.youtube.com/watch?v=KKeBElJS6-M" target="_blank" rel="noreferrer">rapid transport</a>, <a href="https://www.youtube.com/watch?v=aPuYXyJet38" target="_blank" rel="noreferrer">optimization techniques</a>). That maps cleanly to our browser plan where velocity should directly control prefetch radius, interior activation range, and per-frame spawn budget.</p>
<p>Another takeaway is hard separation between topology data and rendered objects. The maps and addresses implementation uses a global topology controller that can answer location and address queries for unloaded regions (<a href="https://www.youtube.com/watch?v=6dLn1GQpu2c" target="_blank" rel="noreferrer">maps and addresses</a>). This is exactly the pattern we need for server-authoritative routing, POI lookup, and world-level queries that should not depend on what one client currently has in memory.</p>
<p>The NPC work is also highly relevant. The million-NPC system computes coarse schedule state globally, then only simulates expensive behavior in player-adjacent space (<a href="https://www.youtube.com/watch?v=nBV0yAAJUf0" target="_blank" rel="noreferrer">million persistent NPCs</a>, <a href="https://www.youtube.com/watch?v=eUi7DB1ar3s" target="_blank" rel="noreferrer">behind the scenes</a>). For us, that reinforces a two-tier simulation model: cheap deterministic far-field state, rich near-field behavior inside AOI.</p>
<p>Finally, Vuntra City's environment design reinforces something easy to forget in technical planning: distribution design is content design. The project avoids uniform random placement, uses weighted outliers for surprise, and drives discovery through diegetic maps and addresses instead of omnipresent minimap markers (<a href="https://www.youtube.com/watch?v=4MZ5-KQW3pc" target="_blank" rel="noreferrer">procedural environments don't have to be boring</a>, <a href="https://www.youtube.com/watch?v=ixR1hqZJlv4" target="_blank" rel="noreferrer">where we're going we won't need a minimap</a>).</p>
<h3 id="browser-games-that-went-massive" tabindex="-1">Browser Games That Went Massive <a class="header-anchor" href="#browser-games-that-went-massive" aria-label="Permalink to &quot;Browser Games That Went Massive&quot;"></a></h3>
<p><strong>Agar.io</strong> (2015) proved that browser multiplayer can reach millions. At its peak, it had over 100,000 concurrent players across servers. The game is 2D and mechanically simple (grow by absorbing smaller cells), but the networking architecture handles massive concurrency through spatial partitioning. Each server runs a region of the game world. Players only receive updates about entities in their view. This is the same interest management pattern needed for a 3D open world, just in 2D.</p>
<p><strong>Slither.io</strong> built on Agar.io's success and proved the model scales. 67 million monthly active users at peak. The game uses WebSocket for real-time position sync and spatial partitioning to limit network traffic. One detail worth noting: Slither.io's server-side collision detection runs at a lower tick rate for distant players (5 Hz) than for nearby ones (30 Hz). This variable tick rate by distance is applicable to a 3D open world.</p>
<p><strong>Surviv.io</strong> was a browser-based battle royale that reached 50 million monthly players before being acquired by Kongregate. It ran a full 80-player battle royale match entirely in the browser with real-time networked physics, destructible environments, and item pickup. The map was procedurally arranged from pre-designed building templates, which is a pattern we could use for creator-placed structures.</p>
<p><strong>Zombs Royale</strong> ran a 100-player battle royale in the browser with fast load times and responsive networking. Like Surviv.io, it proved that large player counts in real-time browser games are commercially viable, not just technically possible.</p>
<p>The common thread across all these browser hits: they load fast (under 5 seconds), they work on any device, the art style is simple but consistent, and the networking is optimized for the specific gameplay (spatial partitioning, variable update rates, aggressive culling of distant state).</p>
<h3 id="runescape-an-mmo-that-moved-to-the-browser" tabindex="-1">RuneScape: An MMO That Moved to the Browser <a class="header-anchor" href="#runescape-an-mmo-that-moved-to-the-browser" aria-label="Permalink to &quot;RuneScape: An MMO That Moved to the Browser&quot;"></a></h3>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/YX7VjGC-2q4" title="Old School RuneScape Steam Launch Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">RuneScape: a full MMO with 20+ years of content running in a browser tab. Custom binary protocol, tile-based streaming, 2,000 concurrent players per server. Proof that browser MMOs work at scale.</div>
<p>RuneScape is the most important case study for a browser-based open world because it actually happened. Jagex moved an entire MMO with 20 years of content into the browser.</p>
<p>RuneScape originally ran as a Java applet. When browsers dropped Java support, Jagex rebuilt the client in C++ and also shipped a fully functional HTML5/WebGL client. Old School RuneScape (the retro version) now runs entirely in the browser via a client compiled through Emscripten to WebAssembly. The game handles large persistent worlds, real-time multiplayer with hundreds of players per server, an economy with a functioning grand exchange (auction house), and 23 skills with deep progression systems.</p>
<p><strong>Technical details that matter:</strong></p>
<ul>
<li>The world is divided into map squares (64x64 tile regions). The client loads a 13x13 grid of regions around the player (104x104 tiles visible). Regions outside this grid are culled completely.</li>
<li>Terrain is tile-based with height values per tile corner. Terrain overlays (paths, water edges, beach transitions) use a shape system with 12 rotation variants per shape. This is more constrained than a heightmap but extremely compact and fast to stream.</li>
<li>The network protocol is custom binary over WebSocket. Each packet type has a defined structure. Player position updates use 2 bytes for map square coordinates and variable-length encoding for movement type. Chat, trade, and combat events have their own compact binary formats. The entire protocol is heavily optimized for minimum bandwidth.</li>
<li>Object rendering uses a model system where the server sends a model ID and the client renders the cached model. Most models are loaded once and reused. This means the world streams as metadata (what model goes where) rather than streaming geometry.</li>
<li>Each server instance handles 2,000 concurrent players across the full game world. The world is not spatially sharded. A single server process manages all players, all NPCs, and all game logic on a 600ms game tick. This works because the game logic is simple per-tick: process player actions, update NPC AI, resolve combat, broadcast state changes.</li>
</ul>
<p><strong>What RuneScape proves for our case:</strong> A full MMO with persistent world state, thousands of players, complex game systems, and a real economy can run in a browser tab. The client is under 50 MB downloaded, loads in a few seconds, and runs on laptops. If a 20-year-old Java MMO can make the transition, a purpose-built browser world has even fewer constraints.</p>
<p><strong>Where RuneScape's approach doesn't fit:</strong> RuneScape's rendering is isometric/fixed-camera, not first/third-person 3D. The visual fidelity is low by modern standards. And the world is not creator-editable. But the networking architecture, the tile-based streaming, and the proof that browser MMOs retain players for decades are all directly relevant.</p>
<h3 id="habbo-hotel-social-spaces-that-lasted-25-years" tabindex="-1">Habbo Hotel: Social Spaces That Lasted 25 Years <a class="header-anchor" href="#habbo-hotel-social-spaces-that-lasted-25-years" aria-label="Permalink to &quot;Habbo Hotel: Social Spaces That Lasted 25 Years&quot;"></a></h3>
<p>Habbo Hotel launched in 2000 and is still running. It's a 2D isometric social world where users create and decorate rooms, visit other people's rooms, and socialize. At peak, it had 9 million monthly users. The entire experience ran in Flash (now HTML5 after the Flash deprecation).</p>
<p>Habbo matters because of how long it has sustained a creator community. The room system is effectively a 2D version of what we're building: users place furniture objects in a grid, customize the layout, and invite others to visit. The economic model (users buy virtual furniture with real money) has generated over $1 billion in lifetime revenue.</p>
<p><strong>What Habbo teaches:</strong></p>
<ul>
<li>Room-based spaces with user-created interiors work as a social platform for decades if the creation tools are simple and the social features are strong.</li>
<li>Virtual furniture economy sustains long-term engagement. Users buy, trade, and collect items. The items have no gameplay utility. They're pure self-expression and status.</li>
<li>Moderation in social spaces requires constant investment. Habbo has been through multiple moderation crises. Automated content filtering plus human moderators plus community reporting is the minimum viable approach.</li>
<li>The transition from Flash to HTML5 (completed around 2020-2021) proved that a large social world can migrate rendering technology without losing its community. The users care about their rooms and friends, not the underlying tech.</li>
</ul>
<h3 id="among-us-and-spatial-social-games" tabindex="-1">Among Us and Spatial Social Games <a class="header-anchor" href="#among-us-and-spatial-social-games" aria-label="Permalink to &quot;Among Us and Spatial Social Games&quot;"></a></h3>
<p>Among Us is not an open world, but its success revealed something important about multiplayer spaces: players want to be in a place together, not just in a game together. The spatial proximity chat mods that went viral showed that being in the same virtual room with directional audio transforms multiplayer from a game mechanic into a social experience.</p>
<p>Spatial social features that enhance a creator world:</p>
<ul>
<li><strong>Proximity voice chat</strong> where volume fades with distance. Walk up to someone to talk. Walk away and they fade out. This creates natural social clusters without requiring voice channel management.</li>
<li><strong>Emote and gesture systems</strong> let players express themselves without voice. A wave, a dance, a pointing gesture. These are cheap to implement (animations on the player avatar) and disproportionately increase social engagement.</li>
<li><strong>Shared activities</strong> that happen in-world (not through menus) turn a space into a venue. If two creators can sit at a virtual table and look at 3D models together, the world has a reason to exist beyond displaying static content.</li>
</ul>
<h3 id="browser-games-that-scaled-on-poki-and-crazygames" tabindex="-1">Browser Games That Scaled on Poki and CrazyGames <a class="header-anchor" href="#browser-games-that-scaled-on-poki-and-crazygames" aria-label="Permalink to &quot;Browser Games That Scaled on Poki and CrazyGames&quot;"></a></h3>
<p>Poki and CrazyGames together serve over 150 million monthly players. The games that perform best on these platforms offer insights into what works in the browser specifically.</p>
<p><strong>Top-performing patterns on browser game portals:</strong></p>
<ul>
<li>Instant play (under 3 seconds to interactive). No login required. No tutorial required. The game should make sense within 5 seconds of landing.</li>
<li>Session flexibility. Players drop in for 2 minutes or 2 hours. The game accommodates both. For a creator world, this means the world should be explorable without committing to a session. Walk around, see cool things, leave. Or stay and build for hours.</li>
<li>Mobile compatibility. Over 60% of Poki traffic is mobile. A browser world that only works on desktop loses the majority of potential visitors.</li>
<li>Social features that don't require friends. Leaderboards, reactions to other players' content, asynchronous features (see what other players built without being online at the same time).</li>
</ul>
<p>The most successful 3D games on these portals (like Shell Shockers, 1v1.LOL, Smash Karts) keep the poly count low, the textures simple, and the framerate high. They prove that players accept simple graphics if the experience is smooth and responsive.</p>
<h3 id="browser-based-creator-platforms" tabindex="-1">Browser-Based Creator Platforms <a class="header-anchor" href="#browser-based-creator-platforms" aria-label="Permalink to &quot;Browser-Based Creator Platforms&quot;"></a></h3>
<p><strong>Hubs by Mozilla (now community-maintained).</strong> Multi-user 3D spaces in the browser built on Three.js and A-Frame. Supports voice chat, avatars, and shared objects. Not an open world (it's room-based), but the networking and rendering architecture are relevant. Mozilla open-sourced it before shutting down the hosted service, so the full codebase is available to study. <a href="https://github.com/mozilla/hubs" target="_blank" rel="noreferrer">GitHub</a>.</p>
<p><strong>Hyperfy.</strong> A web-based metaverse platform running entirely in the browser. Three.js rendering, multiplayer, avatar customization, and world building. Closer to our target than Hubs because it emphasizes creator tools. Worlds load in a browser tab with no download required. <a href="https://hyperfy.io" target="_blank" rel="noreferrer">hyperfy.io</a>.</p>
<p><strong>Ethereal Engine (formerly XREngine).</strong> Open-source engine for multi-user worlds, built on Three.js and bitECS. Supports WebXR, spatial audio, and world editing. Has built-in ECS architecture, networking layer, and editor tools. The closest existing open-source project to what we're describing. Worth studying for how they integrate bitECS with Three.js for entity management and how their networking handles spatial state. <a href="https://github.com/EtherealEngine/etherealengine" target="_blank" rel="noreferrer">GitHub</a>.</p>
<p><strong>Dusk (formerly Rune).</strong> Multiplayer game SDK for web games. Handles the networking layer so developers focus on gameplay. Their state sync approach uses predicted state with server reconciliation, which is the standard model for responsive multiplayer. The SDK abstracts away the complexity of rollback netcode. Worth studying for their developer experience.</p>
<p><strong>Niantic Studio.</strong> A browser-based visual editor and web game engine (the successor to 8th Wall's tooling) for building 3D and XR experiences that others can visit in a browser. It demonstrates that non-technical creators can build 3D scenes in a browser if the tools are approachable. (Niantic spun its geospatial work into Niantic Spatial and sold its games business, including Pokemon GO, to Scopely in 2025.)</p>
<p><strong>PlayCanvas Editor.</strong> Not a game itself, but PlayCanvas's cloud-based 3D editor shows that collaborative world-building tools can run in the browser. Multiple team members edit the same scene simultaneously. The editor communicates changes via a real-time sync layer. This is the collaborative creation model we'd need, except with our world as the canvas instead of a game editor.</p>
<h3 id="native-creator-platforms-lessons-for-browser" tabindex="-1">Native Creator Platforms (Lessons for Browser) <a class="header-anchor" href="#native-creator-platforms-lessons-for-browser" aria-label="Permalink to &quot;Native Creator Platforms (Lessons for Browser)&quot;"></a></h3>
<p>These platforms run as native apps, but their design decisions about creator tools, world persistence, and social dynamics are directly applicable.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/mUgtVhbSiNM" title="Roblox Creator Platform" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Roblox: 80M+ daily users, $740M paid to creators in 2023. Creation happens in-engine. Discovery is social. The business model that proves creator platforms can sustain themselves.</div>
<p><strong>Roblox</strong> is the single most important reference for a creator world. 80+ million daily active users. Creators build full 3D experiences (games, social spaces, stores) that other players visit. The platform handles hosting, networking, discovery, and monetization.</p>
<p>What Roblox gets right:</p>
<ul>
<li><strong>Creation is in-engine.</strong> Roblox Studio is the same environment players experience. Creators test their work instantly. There's no export/upload/wait cycle. For a browser world, the editor should be the world itself.</li>
<li><strong>Scripting is accessible.</strong> Lua (Roblox's scripting language) is simple enough that children learn it. Complex behavior is possible but optional. The floor is low and the ceiling is high.</li>
<li><strong>Discovery is social.</strong> You find experiences because friends are playing them. The homepage shows trending experiences. For a browser world, the world itself is the discovery surface. You explore and find things by walking around.</li>
<li><strong>Monetization works.</strong> Creators earn real money (Roblox paid out $740 million to creators in 2023). This attracts serious creative effort. Without economic incentive, creator platforms become hobby projects that fade.</li>
<li><strong>Roblox's rendering engine is custom</strong> and runs in a native app, not a browser. But the per-experience asset budgets are modest by modern standards (100 MB recommended max). Most successful Roblox experiences use low-poly stylized art, which aligns with browser rendering constraints.</li>
</ul>
<p><strong>Fortnite Creative / UEFN (Unreal Editor for Fortnite).</strong> Epic brought Unreal Engine's full editor to Fortnite creators. The result is a platform where people build islands (self-contained worlds) using professional-grade tools. Fortnite handles hosting, multiplayer, and distribution.</p>
<p>Relevant insights:</p>
<ul>
<li><strong>Professional tools attract professional content.</strong> UEFN produces visually stunning experiences because creators have access to UE5's full capabilities. The trade-off is complexity. UEFN has a steep learning curve.</li>
<li><strong>Island-based instancing</strong> (each creation is a separate world) avoids the moderation and conflict problems of a single shared world. But it also means creators don't naturally discover each other's work by exploring. You visit islands through menus, not by walking.</li>
<li><strong>The business model works.</strong> Fortnite's creator program pays based on engagement. Top creators earn millions per year. Again, economic incentive drives quality.</li>
</ul>
<p><strong>Dreams (Media Molecule / PlayStation).</strong> Dreams gave console players a full 3D creation suite (modeling, animation, music, logic, level design) and a platform to share creations. It's the most ambitious creator-platform ever built in terms of tool depth.</p>
<p>Relevant insights:</p>
<ul>
<li><strong>Sculpture-based modeling</strong> instead of polygon editing. Creators shape soft volumes with move/grab/smooth tools, similar to ZBrush but more intuitive. The learning curve is gentle. This approach maps well to in-browser creation because it doesn't require understanding vertices and UV maps.</li>
<li><strong>Everything is a shared asset.</strong> If someone creates a tree model, anyone can use it in their own creation (with attribution). This creates a compound creative ecosystem where each creation makes the platform more valuable.</li>
<li><strong>Dreams struggled commercially</strong> despite critical praise. The problem was distribution: it was locked to PlayStation, and the creation tools were so deep that most players never moved beyond consuming content. The lesson: the creation tools need to be simple enough that the majority of users try them, even if only a minority become serious builders.</li>
</ul>
<p><strong>Core (Manticore Games).</strong> A free platform for creating and playing multiplayer games, built on Unreal Engine with a simplified editor. Core's editor runs as a native app, but the philosophy is relevant. Templates and community-shared scripts let beginners assemble games from pre-made components. Advanced creators can write Lua scripts for custom behavior. Core struggled to find a large audience, partly because the games had to be played through the Core launcher. A browser-based version wouldn't have this friction.</p>
<p><strong>VRChat</strong> and <strong>Rec Room</strong> are social platforms where creators build spaces others visit. VRChat uses Unity and runs on PC/VR. Rec Room runs on everything including mobile. Both prove that user-generated 3D worlds can sustain large communities. VRChat is more technically impressive (custom shaders, complex avatars). Rec Room is more accessible (in-app creation tools, simpler graphics, wider platform support). For a browser world, Rec Room's approach of simple in-app creation tools is more applicable than VRChat's external-tool workflow.</p>
<div class="video-embed">
<iframe src="https://www.youtube.com/embed/E5HCeVCjCXo" title="Second Life: Your World, Your Imagination" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<div class="game-caption">Second Life: launched in 2003, still running with 200K+ daily users. Entirely user-created. Parcel-based ownership, virtual economy worth ~$500M/year. 20 years of lessons in persistence and moderation.</div>
<p><strong>Second Life</strong> is the grandfather of all creator worlds. Launched in 2003, it's still running with 200,000+ daily active users. The entire world is user-created. Land is owned and traded. Creators sell objects, clothing, and buildings. The in-world scripting language (LSL) enables interactive content.</p>
<p>What Second Life teaches after 20+ years:</p>
<ul>
<li><strong>Persistence matters more than graphics.</strong> Second Life's visuals are dated, but the world persists. Creations stay where you put them. Relationships and history accumulate. This persistence is what keeps people coming back.</li>
<li><strong>Economy drives creation.</strong> Second Life's GDP is estimated at $500 million per year. Creators build because they can sell. Without economic incentive, the volume and quality of creator content drops off.</li>
<li><strong>User-generated content requires moderation infrastructure.</strong> Second Life has had 20 years of moderation challenges. Any platform where users can place arbitrary content in a shared space needs automated scanning, reporting tools, and human review.</li>
<li><strong>Land-based spatial organization works.</strong> Second Life divides its world into parcels that users own. Each parcel has a prim (object) limit. This naturally prevents any one creator from consuming all the resources. For a browser world, chunk-based ownership with per-chunk object budgets is the equivalent pattern.</li>
</ul>
<h2 id="comparative-analysis-what-each-game-teaches-us" tabindex="-1">Comparative Analysis: What Each Game Teaches Us <a class="header-anchor" href="#comparative-analysis-what-each-game-teaches-us" aria-label="Permalink to &quot;Comparative Analysis: What Each Game Teaches Us&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Game</th>
<th>Key Lesson</th>
<th>Applicable Tech</th>
<th>Risk If Ignored</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Skyrim</strong></td>
<td>Chunk-based streaming with cell grid</td>
<td>Heightmap terrain, LOD tiers, interior/exterior separation</td>
<td>World doesn't fit in browser memory</td>
</tr>
<tr>
<td><strong>The Witcher 3</strong></td>
<td>Layered world composition by separate teams</td>
<td>Content-aware streaming, impostor rendering</td>
<td>Creators can't work independently</td>
</tr>
<tr>
<td><strong>Breath of the Wild</strong></td>
<td>Systemic rules beat scripted content</td>
<td>Material interaction system, physics-driven gameplay</td>
<td>World feels static and dead</td>
</tr>
<tr>
<td><strong>GTA V</strong></td>
<td>Ambient life makes worlds feel real</td>
<td>NPC behavior systems, traffic, time-of-day</td>
<td>Creator world feels like an empty museum</td>
</tr>
<tr>
<td><strong>Elden Ring</strong></td>
<td>Density variation and asset reuse</td>
<td>Modular asset library, sparse + dense zones</td>
<td>Either too empty or too expensive to fill</td>
</tr>
<tr>
<td><strong>No Man's Sky</strong></td>
<td>Procedural generation for the canvas, creator content for the soul</td>
<td>Seed-based terrain, compact base data model</td>
<td>Infinite but boring terrain</td>
</tr>
<tr>
<td><strong>Minecraft</strong></td>
<td>Fully editable world, simple tools, infinite depth</td>
<td>Chunk streaming, palette compression, block-edit protocol</td>
<td>Creators can't reshape the world itself</td>
</tr>
<tr>
<td><strong>Roblox</strong></td>
<td>Creation is in-engine, economy drives quality</td>
<td>In-world editor, creator monetization</td>
<td>Nobody builds because there's no reason to</td>
</tr>
<tr>
<td><strong>Krunker.io</strong></td>
<td>Browser UGC works at scale with simple tools</td>
<td>Three.js rendering, voxel-based editor, marketplace</td>
<td>Creation tools too complex for casual creators</td>
</tr>
<tr>
<td><strong>Hordes.io</strong></td>
<td>200+ players in browser 3D, solo-dev viable</td>
<td>Custom WebGL, spatial culling, stylized art</td>
<td>Over-engineer the multiplayer layer</td>
</tr>
<tr>
<td><strong>Vuntra City (native reference)</strong></td>
<td>Speed-aware streaming and two-tier world simulation keep a huge procedural city coherent</td>
<td>Velocity-coupled LOD policy, topology query layer, far-field schedule simulation + near-field behavior</td>
<td>High-speed traversal causes pop-in and simulation spikes</td>
</tr>
<tr>
<td><strong>Agar.io / Slither.io</strong></td>
<td>Spatial partitioning enables massive concurrency</td>
<td>Variable tick rate by distance, interest management</td>
<td>Network collapses at scale</td>
</tr>
<tr>
<td><strong>Second Life</strong></td>
<td>Persistence and economy sustain 20-year communities</td>
<td>Parcel-based ownership, object budgets, marketplace</td>
<td>No long-term retention</td>
</tr>
<tr>
<td><strong>Dreams</strong></td>
<td>Sculpture-based creation is more intuitive than polygon editing</td>
<td>Volume-based modeling, shared asset library</td>
<td>Creation tools feel like a CAD program</td>
</tr>
<tr>
<td><strong>Fortnite Creative</strong></td>
<td>Pro tools attract pro content</td>
<td>Full editor capabilities in the platform</td>
<td>Content quality ceiling is too low</td>
</tr>
<tr>
<td><strong>RuneScape</strong></td>
<td>Full MMO works in browser via Wasm, binary protocols</td>
<td>Emscripten, custom binary WebSocket protocol, tile streaming</td>
<td>Underestimate what browsers can handle</td>
</tr>
<tr>
<td><strong>Habbo Hotel</strong></td>
<td>Simple room creation sustains 25-year community</td>
<td>Grid-based placement, virtual furniture economy</td>
<td>Overcomplicate the creation tools</td>
</tr>
</tbody>
</table>
<p>The games that matter most for our specific case (browser-based, creator-focused, multiplayer) are Minecraft (editable world, compact data), Roblox (in-engine creation, economy), Krunker (browser UGC at scale), and Hordes.io (browser MMO architecture). The AAA titles (Skyrim, BotW, Witcher 3) teach rendering and streaming. The browser hits (Agar.io, Slither.io, Surviv.io) teach networking at scale. The creator platforms (Roblox, Dreams, Second Life) teach community dynamics. Vuntra City adds a current, implementation-level reference for speed-aware streaming, diegetic navigation, and million-agent simulation patterns in a modern procedural city.</p>
<h2 id="what-skyrim-and-the-witcher-would-look-like-in-a-browser" tabindex="-1">What Skyrim and The Witcher Would Look Like in a Browser <a class="header-anchor" href="#what-skyrim-and-the-witcher-would-look-like-in-a-browser" aria-label="Permalink to &quot;What Skyrim and The Witcher Would Look Like in a Browser&quot;"></a></h2>
<p>Let's get concrete. If you took Skyrim's Whiterun and rebuilt it for browser delivery:</p>
<p><strong>Terrain:</strong> The area around Whiterun is roughly 2km x 2km. At our chunk size (64m), that's about 32x32 = 1024 chunks. At 2-4 KB per chunk's heightmap, that's 2-4 MB of terrain data. The terrain textures (grass, dirt, rock, snow) as KTX2 atlas tiles might add another 5 MB. Total terrain: under 10 MB for the entire region.</p>
<p><strong>Structures:</strong> Whiterun itself has maybe 40-50 buildings. Each building as an optimized GLB (3 LOD levels) might be 200-500 KB at highest detail. But you only need full detail for the 5-10 closest buildings. The rest are medium or low LOD (50-100 KB each). Total visible structures at any time: 2-5 MB.</p>
<p><strong>Foliage:</strong> Skyrim's trees and grass around Whiterun are all instanced. You need maybe 10 unique tree models (200 KB each at full LOD, 20 KB as billboard impostors) and a grass system that generates blades from a density map on the GPU. Total foliage assets: 2-3 MB. The instancing data (positions, rotations, scales) for a 5x5 chunk area: under 500 KB.</p>
<p><strong>NPCs:</strong> Whiterun has roughly 70 named NPCs plus guards. Each avatar at medium quality: 100-200 KB. But only 10-20 are visible at any time. Total NPC rendering data: 2-4 MB.</p>
<p><strong>Grand total for a Whiterun-scale area visible at any time: 15-25 MB.</strong> That's absolutely viable in a browser. The initial load would show terrain and major structures within 3-5 seconds on broadband, with detail filling in over the next few seconds.</p>
<p>The Witcher 3's Novigrad is larger and denser but the same principles apply. You'd need more aggressive LOD and streaming, but the total visible data at any moment stays within browser memory limits.</p>
<h2 id="what-we-d-build-first" tabindex="-1">What We'd Build First <a class="header-anchor" href="#what-we-d-build-first" aria-label="Permalink to &quot;What We'd Build First&quot;"></a></h2>
<p>A full open world is a multi-year project. Here's the path to get something real in creators' hands quickly:</p>
<p><strong>Phase 1: Shared Island (3 months).</strong> A single terrain chunk (512x512m island) with heightmap terrain, water, basic foliage, day/night cycle. Multiplayer via Durable Objects (up to 50 concurrent users). Creators can place AI-generated 3D assets from their existing Cinevva library. Think of it as a shared diorama.</p>
<p><strong>Phase 2: Expandable World (3 months).</strong> Chunk streaming for a 4x4 km world. Creator-owned plots where they have edit permissions. LOD system for terrain and objects. Persistent world state. Up to 200 concurrent users across the world.</p>
<p><strong>Phase 3: Living World (6 months).</strong> AI-assisted terrain sculpting. Procedural foliage and atmosphere. Quest/event system so creators can build interactive experiences, not just static scenes. Voice chat. Avatar customization. The world becomes a destination, not a demo.</p>
<h2 id="open-questions" tabindex="-1">Open Questions <a class="header-anchor" href="#open-questions" aria-label="Permalink to &quot;Open Questions&quot;"></a></h2>
<p><strong>Art style.</strong> Stylized (low-poly, cel-shaded) is cheaper to render and more forgiving of AI-generated assets. Realistic requires higher-quality assets and more rendering budget. Skyrim worked despite dated graphics because the art direction was consistent. BotW is gorgeous on tablet-class hardware because the cel-shaded style hides low polygon counts. Minecraft uses 16x16 textures and is one of the most recognizable games ever. Krunker.io and Hordes.io both succeed with simple stylized graphics in the browser. The evidence overwhelmingly favors a stylized direction for a browser world. We need to pick a specific visual identity early and enforce consistency across AI-generated assets.</p>
<p><strong>World persistence vs. instancing.</strong> Does everyone share one world (like an MMO or Second Life) or do creators each get their own instance that others can visit (like Minecraft servers or Fortnite Islands)? The tech supports both, but the social dynamics are completely different. Second Life's persistent shared world creates serendipitous discovery (you stumble onto other people's work by walking around). Fortnite's instanced islands require a menu or portal system for discovery. Roblox uses a hub-based approach: games are separate but you browse and discover through a shared interface. A hybrid could work: a persistent shared overworld where creators own plots (like Second Life parcels), with the option to enter standalone experiences through portals.</p>
<p><strong>Creation tool depth.</strong> Dreams proved that deep creation tools impress critics but intimidate users. Townscaper proved that minimal tools can sell a million copies. Roblox Studio sits in the middle: simple enough for kids, deep enough for professionals. Krunker's voxel editor is simpler still. For a browser world, we should start with Townscaper-level simplicity (place objects, they snap and connect) and add depth over time. The initial experience of placing your first object in the world should take under 30 seconds.</p>
<p><strong>Economy.</strong> Roblox, Second Life, and Fortnite Creative all prove that economic incentive is what turns a toy into a platform. Without a way for creators to earn from their work, the most talented creators will build elsewhere. This doesn't need to launch on day one, but the architecture should support it (object ownership, visit tracking, creator attribution).</p>
<p><strong>Mobile.</strong> WebGPU on mobile is years away from reliable. A mobile experience would need to be a reduced version: simpler terrain, fewer objects, shorter view distance. Rec Room's approach of running on everything with adaptive quality is worth studying. Or we ship a native app for mobile and keep the full experience in the browser.</p>
<p><strong>Moderation.</strong> An open world where anyone can place anything is a moderation nightmare. Second Life has been dealing with this for 20 years. Every placed asset needs automated content review before it becomes visible to others. This adds latency to the creative process but is non-negotiable. We should also consider parcel-based content ratings (like Second Life's General/Moderate/Adult system) so creators can choose their content boundaries.</p>
<p><strong>Systemic interactions.</strong> BotW and Minecraft both show that material-based interaction systems create exponentially more interesting worlds than static object placement. If a creator places a wooden bridge and another creator starts a fire nearby, should the bridge burn? If someone places a dam, should water pool behind it? These interactions make the world feel alive but require consistent physics and material rules across all creator content. Deciding how far to go with systemic design is an early architectural decision.</p>
<h2 id="key-takeaways" tabindex="-1">Key Takeaways <a class="header-anchor" href="#key-takeaways" aria-label="Permalink to &quot;Key Takeaways&quot;"></a></h2>
<p>Browser 3D open worlds are viable today. Hordes.io runs 200+ players in 3D in a browser. Krunker.io had 10 million monthly players with a full 3D map editor. The .io games proved browser multiplayer scales to millions. The tech isn't speculative.</p>
<p>The rendering is ready. Three.js and Babylon.js handle 3D scenes comparable to early-2010s AAA games. WebGPU unlocks compute shaders for terrain and foliage. Wasm physics engines run within 2-3x of native. A Whiterun-scale area fits in 15-25 MB of visible data.</p>
<p>The networking is ready. Cloudflare Durable Objects give you per-chunk authoritative servers at the edge. CRDTs handle collaborative editing without conflicts. Spatial partitioning (proven by every game from Agar.io to Skyrim) keeps network traffic manageable at hundreds of concurrent players.</p>
<p>The AAA open worlds (Skyrim, Witcher 3, BotW, Elden Ring, Minecraft, No Man's Sky) aren't just graphical benchmarks. They're textbooks on chunk streaming, LOD management, procedural generation, systemic design, and world composition. Every technique they use has a browser-compatible equivalent.</p>
<p>The creator platforms (Roblox, Second Life, Fortnite Creative, Dreams) teach the social and economic lessons. Creation must happen in-world, not in an external tool. Economic incentive drives quality. Persistence creates attachment. Simple tools reach more creators than powerful ones.</p>
<p>The creative pipeline is where Cinevva has an edge. We already generate 3D assets, textures, and audio. Connecting that pipeline to a world placement system is the missing piece. AI-generated content fills the world. Creator curation and arrangement gives it soul.</p>
<p>What we'd end up with isn't Skyrim in a browser. It's closer to the intersection of Minecraft (editable world), Roblox (creator economy), and BotW (systemic interactions), running in a browser tab with AI-powered creation tools. The technology to build it exists. The question is execution.</p>
<h2 id="research-papers-and-academic-references" tabindex="-1">Research Papers and Academic References <a class="header-anchor" href="#research-papers-and-academic-references" aria-label="Permalink to &quot;Research Papers and Academic References&quot;"></a></h2>
<p>The techniques in this guide aren't invented from scratch. They're grounded in decades of research. These are the papers that matter most for each subsystem, with notes on how they apply to a browser open world.</p>
<h3 id="terrain-generation-and-rendering" tabindex="-1">Terrain Generation and Rendering <a class="header-anchor" href="#terrain-generation-and-rendering" aria-label="Permalink to &quot;Terrain Generation and Rendering&quot;"></a></h3>
<p><strong>&quot;An Image Synthesizer&quot;</strong> -- Ken Perlin (SIGGRAPH 1985). <a href="https://doi.org/10.1145/325165.325247" target="_blank" rel="noreferrer">DOI</a>. The paper that introduced Perlin noise. Every procedural terrain generator in every game since 1985 traces back to this. The noise function produces the smooth randomness that, when layered in octaves (fractional Brownian motion), generates natural-looking heightmaps. Simplex noise (Perlin, 2001) is the faster successor. For our terrain pipeline, this is the foundation: noise-based heightmap generation runs in a WebGPU compute shader at interactive speeds.</p>
<p><strong>&quot;Texturing and Modeling: A Procedural Approach&quot;</strong> -- Ebert, Musgrave, Peachey, Perlin, Worley (1994, 3rd edition 2003). The textbook on procedural generation. Musgrave's chapters on terrain modeling, including multifractal terrain with erosion-like features, are the direct basis for modern game terrain generators. The fBm parameters (lacunarity, persistence, octave count) described here are the same ones we'd expose to creators for terrain customization.</p>
<p><strong>&quot;Geometry Clipmaps: Terrain Rendering Using Nested Regular Grids&quot;</strong> -- Losasso and Hoppe (SIGGRAPH 2004). <a href="https://doi.org/10.1145/1015706.1015799" target="_blank" rel="noreferrer">DOI</a>. This paper solved how to render massive terrain at interactive rates using concentric LOD rings (clipmaps). The terrain mesh is a fixed set of nested grids centered on the camera. As the camera moves, the grids shift and update. This is the technique recommended in our terrain section for WebGL 2, and it works because the GPU workload is constant regardless of world size. The original implementation predates WebGL but maps directly to it.</p>
<p><strong>&quot;Fast Hydraulic Erosion Simulation and Visualization on GPU&quot;</strong> -- Mei, Decaudin, Hu (2007). <a href="https://hal.inria.fr/inria-00402079/document" target="_blank" rel="noreferrer">PDF</a>. Moved hydraulic erosion from a CPU-bound offline process to a real-time GPU computation. The paper's shallow-water simulation model (treating water as a height field, computing flow between grid cells) runs in a compute shader. For our pipeline, server-side GPU erosion can transform noise-generated terrain into geologically plausible landscapes in under a second, making AI-generated terrain look hand-sculpted.</p>
<p><strong>&quot;Real-Time Rendering of Procedurally Generated Planets&quot;</strong> -- Hybrid approaches from No Man's Sky's GDC talks and Sean Murray's descriptions. While not a single paper, the GDC 2017 talk &quot;Building Worlds Using Maths&quot; by Innes McKendrick (Hello Games) details how No Man's Sky generates planet-scale terrain using stacked noise functions, voxel representation with marching cubes, and GPU-side generation. Directly relevant to our procedural terrain approach, particularly for generating terrain features that heightmaps can't represent (caves, arches).</p>
<p><strong>&quot;C-DBLOD: Hybrid LOD for Terrain Rendering&quot;</strong> -- Filip Strugar (2014). <a href="https://www.vertexasylum.com/CDLOD/cdlod_latest.pdf" target="_blank" rel="noreferrer">Paper</a>. An improvement on geometry clipmaps that adds quadtree-based selection for better adaptivity around camera position. The key insight: instead of fixed concentric rings, use a quadtree to select terrain patches at varying resolutions. This handles irregular terrain (where some areas need more detail than others) better than pure clipmaps. Implementable in WebGL 2 with a small CPU-side quadtree traversal.</p>
<h3 id="_3d-gaussian-splatting-and-neural-rendering" tabindex="-1">3D Gaussian Splatting and Neural Rendering <a class="header-anchor" href="#_3d-gaussian-splatting-and-neural-rendering" aria-label="Permalink to &quot;3D Gaussian Splatting and Neural Rendering&quot;"></a></h3>
<p><strong>&quot;3D Gaussian Splatting for Real-Time Radiance Field Rendering&quot;</strong> -- Kerbl, Kopanas, Leimkühler, Drettakis (SIGGRAPH 2023). <a href="https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/" target="_blank" rel="noreferrer">Project page</a>. The paper that launched the Gaussian splatting revolution. A scene is represented as millions of 3D Gaussians, each with position, covariance (shape), opacity, and spherical harmonic color coefficients. Rendering sorts splats by depth and rasterizes them as 2D Gaussians. The approach is 100-1000x faster to train than NeRFs and renders at real-time rates. Multiple WebGL/WebGPU implementations exist. For our platform, this enables creators to capture real-world objects with phone photos and place them in the browser world.</p>
<p><strong>&quot;NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis&quot;</strong> -- Mildenhall et al. (ECCV 2020). <a href="https://www.matthewtancik.com/nerf" target="_blank" rel="noreferrer">Project page</a>. The foundational neural scene representation paper. A neural network maps 3D coordinates to color and density, enabling photorealistic novel view synthesis from a set of input photographs. While NeRFs are too expensive to render directly in a browser (they require per-pixel network evaluation), the NeRF-to-mesh extraction pipeline (training a NeRF, then running marching cubes on the density field) produces high-quality textured meshes from photos.</p>
<p><strong>&quot;Instant Neural Graphics Primitives with a Multiresolution Hash Encoding&quot;</strong> -- Müller, Evans, Schied, Keller (SIGGRAPH 2022). <a href="https://nvlabs.github.io/instant-ngp/" target="_blank" rel="noreferrer">Project page</a>. Reduced NeRF training from hours to seconds using a multi-resolution hash table for spatial encoding. This made NeRF practical for production use. The hash encoding technique is also applicable to other spatial data in a browser world (fast lookups in large 3D datasets).</p>
<p><strong>&quot;Neuralangelo: High-Fidelity Neural Surface Reconstruction&quot;</strong> -- Li et al. (CVPR 2023). <a href="https://research.nvidia.com/labs/dir/neuralangelo/" target="_blank" rel="noreferrer">Project page</a>. Extracts high-quality triangle meshes from neural representations using multi-resolution hash encoding and numerical gradients for SDF (signed distance function) estimation. The output meshes are directly usable in browser 3D engines. For our asset pipeline, Neuralangelo (or similar tools like NeuS2) can convert NeRF captures into web-ready GLB files with clean geometry and baked textures.</p>
<h3 id="multiplayer-networking-and-state-synchronization" tabindex="-1">Multiplayer Networking and State Synchronization <a class="header-anchor" href="#multiplayer-networking-and-state-synchronization" aria-label="Permalink to &quot;Multiplayer Networking and State Synchronization&quot;"></a></h3>
<p><strong>&quot;Interest Management in Massively Multiplayer Online Games&quot;</strong> -- Boulanger, Kienzle, Verbrugge (2006). <a href="https://doi.org/10.1007/11558651_18" target="_blank" rel="noreferrer">DOI</a>. A comprehensive survey of area-of-interest (AOI) management techniques for MMOs. Covers grid-based, aura-based, and hybrid approaches for filtering network updates based on spatial relevance. The grid-based approach (which maps to our chunk system) is the most efficient for uniform-density worlds. The aura-based approach (per-entity influence radius) works better for variable density. Our recommendation of chunk-based AOI with priority-based update rates within the AOI draws from this research.</p>
<p><strong>&quot;Dead Reckoning: Latency Hiding for Networked Games&quot;</strong> -- Pantel and Wolf (2002). <a href="https://doi.org/10.1145/566500.566510" target="_blank" rel="noreferrer">DOI</a>. Formalizes dead reckoning (predicting entity positions based on last known velocity) for networked games. The paper quantifies the trade-off: higher prediction thresholds reduce bandwidth but increase position error visible during corrections. For browser games with 50-200ms latency, a prediction threshold of 0.5-1.0 meters keeps corrections invisible while cutting position update bandwidth by 60-80%.</p>
<p><strong>&quot;Conflict-free Replicated Data Types&quot;</strong> -- Shapiro, Preguiça, Baquero, Zawirski (2011). <a href="https://doi.org/10.1007/978-3-642-24550-3_29" target="_blank" rel="noreferrer">DOI</a>. The foundational CRDT paper. Defines state-based and operation-based CRDTs that converge without coordination. For our world editing system, the relevant CRDTs are: LWW-Register (Last-Writer-Wins Register) for object properties that have a single value (position, rotation, color), and OR-Set (Observed-Remove Set) for the collection of objects in a chunk (handles concurrent add/remove without conflicts). Yjs implements these efficiently in JavaScript.</p>
<p><strong>&quot;Time Warp: A Mechanism for Distributed Simulation&quot;</strong> -- Jefferson (1985). <a href="https://doi.org/10.1145/3916.3988" target="_blank" rel="noreferrer">DOI</a>. The original optimistic distributed simulation paper. While Time Warp itself is too complex for a browser game, the core insight (process events optimistically and roll back if a conflict arrives from another node) is the basis for modern client-side prediction with server reconciliation. This is how every responsive multiplayer game works: the client predicts locally, sends actions to the server, and corrects if the server disagrees.</p>
<p><strong>&quot;The TRIBES Engine Networking Model&quot;</strong> -- Frohnmayer and Gift (GDC 1999). One of the first practical descriptions of client-server game networking with interest management, prioritized state updates, and bandwidth budgeting. The &quot;ghost manager&quot; concept (the server maintains a per-client view of what the client knows, and only sends deltas from that view) is exactly what our chunk-based Durable Object architecture implements. This GDC talk is the intellectual ancestor of most modern game networking.</p>
<p><strong>&quot;Source Multiplayer Networking&quot;</strong> -- Valve (2009). <a href="https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking" target="_blank" rel="noreferrer">Developer documentation</a>. Valve's documentation of the Source engine networking model (used in Half-Life 2, CS:GO, Team Fortress 2). Covers client-side prediction, entity interpolation, lag compensation, and the &quot;snapshot&quot; system where the server sends full world state at regular intervals while the client interpolates between snapshots. This is the gold standard for authoritative server networking and directly applicable to our architecture.</p>
<h3 id="procedural-generation" tabindex="-1">Procedural Generation <a class="header-anchor" href="#procedural-generation" aria-label="Permalink to &quot;Procedural Generation&quot;"></a></h3>
<p><strong>&quot;Model Synthesis: A General Procedural Modeling Algorithm&quot;</strong> -- Merrell (2007). <a href="https://doi.org/10.1145/1289603.1289612" target="_blank" rel="noreferrer">DOI</a>. One of the precursors to Wave Function Collapse. Generates 3D structures from example models by propagating local constraints. The algorithm ensures global consistency by iteratively collapsing cells with the fewest possibilities (minimum entropy heuristic). This is how Townscaper and similar generators produce coherent structures from simple user input.</p>
<p><strong>&quot;WaveFunctionCollapse&quot;</strong> -- Maxim Gumin (2016). <a href="https://github.com/mxgmn/WaveFunctionCollapse" target="_blank" rel="noreferrer">GitHub</a>. Not a traditional paper but a seminal open-source project with extensive documentation. The algorithm takes a small example image or tileset and generates larger outputs that are locally similar to the input. For a creator world, WFC can generate building layouts, road networks, dungeon maps, and terrain details from a small set of creator-defined rules. Multiple JavaScript implementations exist.</p>
<p><strong>&quot;Wave Function Collapse is Constraint Solving in the Wild&quot;</strong> -- Karth and Smith (FDG 2017). <a href="https://doi.org/10.1145/3102071.3110566" target="_blank" rel="noreferrer">DOI</a>. An academic analysis of WFC that clarifies its relationship to constraint satisfaction and shows how to analyze and extend it. Relevant for understanding WFC's limitations (it can get stuck and need backtracking) and how to design tilesets that avoid these issues.</p>
<p><strong>&quot;Superposition Theorem and Its Implications for the Procedural Generation of Game Content&quot;</strong> -- Sandhu et al. (2022). Explores using quantum-inspired superposition concepts for procedural content generation. While speculative, the mathematical framework for maintaining multiple possible states before &quot;collapsing&quot; to a final configuration is exactly how WFC works and could inform more sophisticated generation systems.</p>
<h3 id="real-time-rendering-techniques" tabindex="-1">Real-Time Rendering Techniques <a class="header-anchor" href="#real-time-rendering-techniques" aria-label="Permalink to &quot;Real-Time Rendering Techniques&quot;"></a></h3>
<p><strong>&quot;Real-Time Rendering&quot;</strong> -- Akenine-Möller, Haines, Hoffman (4th edition, 2018). The standard textbook. Chapter 19 (Acceleration Structures), Chapter 20 (Efficient Shading), and Chapter 21 (Virtual and Augmented Reality) are particularly relevant. The frustum culling, occlusion culling, and LOD algorithms described here are what Three.js, Babylon.js, and every game engine implement. Not a paper, but the definitive reference.</p>
<p><strong>&quot;A Survey on Baking Neural Radiance Fields for Real-Time View Synthesis&quot;</strong> -- Reiser et al. (2023). <a href="https://doi.org/10.1111/cgf.14835" target="_blank" rel="noreferrer">DOI</a>. Surveys methods to convert NeRFs into formats that render in real time (meshes, textures, sparse voxel grids). Directly relevant to our asset pipeline where server-side neural capture needs to produce browser-renderable output.</p>
<p><strong>&quot;Scalable and Accurate Online Feature Matching Using 3D Gaussian Splatting&quot;</strong> -- Various groups (2024-2025). Multiple recent papers explore editing, compositing, and dynamic scenes with Gaussian splats. Relevant because creator worlds need to composite multiple splat scenes (each creator's captured objects) into a single coherent scene. Methods for splat editing (recoloring, deformation, compositing) are active research areas.</p>
<p><strong>&quot;Efficient GPU Screen-Space Ray Tracing&quot;</strong> -- McGuire and Mara (JCGT 2014). <a href="https://doi.org/10.2312/hpg.20141090" target="_blank" rel="noreferrer">DOI</a>. The paper behind screen-space reflections used in our water rendering section. Traces rays through the depth buffer for approximate reflections without the cost of full ray tracing. The &quot;hierarchical tracing&quot; variant (using a min-max depth mipmap) runs efficiently on WebGL 2.</p>
<p><strong>&quot;Simulating Ocean Water&quot;</strong> -- Jerry Tessendorf (2001). <a href="https://people.computing.clemson.edu/~jtessen/reports/papers_files/coursenotes2004.pdf" target="_blank" rel="noreferrer">PDF</a>. The foundational paper on FFT-based ocean simulation. Describes the Phillips spectrum (statistical model of ocean waves) and how to transform it into a spatial displacement map via inverse FFT. Used by every major game with realistic ocean (Sea of Thieves, Assassin's Creed, Uncharted). The FFT computation maps cleanly to WebGPU compute shaders.</p>
<p><strong>&quot;Precomputed Atmospheric Scattering&quot;</strong> -- Bruneton and Neyret (EGSR 2008). <a href="https://doi.org/10.1111/j.1467-8659.2008.01245.x" target="_blank" rel="noreferrer">DOI</a>. The paper behind physically accurate sky rendering. Precomputes atmospheric scattering into lookup tables that a fragment shader samples in real time. Produces correct sky colors, aerial perspective (distant objects look bluish/hazy), and sunset/sunrise colors from first principles. The precomputed tables are small (a few hundred KB) and the runtime shader is cheap. Both Three.js's <code>Sky</code> shader and Babylon.js's procedural sky are simplified versions of this approach.</p>
<p><strong>&quot;Ambient Occlusion Volumes&quot;</strong> -- McGuire (HPG 2010) and <strong>&quot;Scalable Ambient Obscurance&quot;</strong> -- McGuire, Mara, Luebke (HPG 2012). <a href="https://research.nvidia.com/sites/default/files/pubs/2012-06_Scalable-Ambient-Obscurance/McGuire12SAO.pdf" target="_blank" rel="noreferrer">PDF</a>. The papers behind modern SSAO implementations. SAO is the most commonly implemented variant in browser 3D engines because it's efficient (one depth buffer sample per pixel) and produces plausible contact shadows. The algorithm samples the depth buffer around each pixel to estimate how &quot;occluded&quot; it is by nearby geometry. Both Three.js and Babylon.js implement SAO-derived SSAO.</p>
<h3 id="crowd-rendering-and-animation" tabindex="-1">Crowd Rendering and Animation <a class="header-anchor" href="#crowd-rendering-and-animation" aria-label="Permalink to &quot;Crowd Rendering and Animation&quot;"></a></h3>
<p><strong>&quot;GPU Crowd Rendering&quot;</strong> -- Dudash (2007) and subsequent GDC/SIGGRAPH presentations on instanced crowd rendering. The core technique: bake skeletal animation frames into textures (Vertex Animation Textures), then render crowds as instanced meshes where each instance reads its bone transforms from the animation texture based on its current frame. This decouples animation evaluation from draw calls, allowing a single instanced draw call to render hundreds of uniquely animated characters.</p>
<p><strong>&quot;Position Based Dynamics&quot;</strong> -- Müller et al. (2007). <a href="https://doi.org/10.1016/j.jvcir.2007.01.005" target="_blank" rel="noreferrer">DOI</a>. The foundational paper on PBD, which is how modern game engines simulate cloth, hair, and soft bodies. Rapier (our recommended Wasm physics engine) uses PBD-derived solvers. For avatar customization (capes, flowing hair, loose clothing), PBD provides responsive simulation at game frame rates. The Wasm implementation keeps it off the main JavaScript thread.</p>
<p><strong>&quot;FABRIK: A Fast, Iterative Solver for the Inverse Kinematics Problem&quot;</strong> -- Aristidou and Lasenby (2011). <a href="https://doi.org/10.1016/j.gmod.2011.05.003" target="_blank" rel="noreferrer">DOI</a>. The paper behind the IK solver recommended in our avatar section. FABRIK works by alternately reaching from end effector to root and root to end effector, converging in 3-5 iterations. It's faster than Jacobian-based IK, handles joint constraints naturally, and is simple to implement (about 50 lines of code for the basic solver). Both Three.js and Babylon.js IK implementations are derived from FABRIK.</p>
<h3 id="virtual-worlds-and-collaborative-environments" tabindex="-1">Virtual Worlds and Collaborative Environments <a class="header-anchor" href="#virtual-worlds-and-collaborative-environments" aria-label="Permalink to &quot;Virtual Worlds and Collaborative Environments&quot;"></a></h3>
<p><strong>&quot;Massive Multiplayer Online Games: A Survey of the State-of-the-Art&quot;</strong> -- Yahyavi and Kemme (2013). <a href="https://doi.org/10.1145/2522968.2522972" target="_blank" rel="noreferrer">DOI</a>. Comprehensive survey of MMO architecture covering client-server models, peer-to-peer approaches, interest management, consistency models, scalability techniques, and cheating prevention. The taxonomy of consistency models (strict, eventual, causal) maps to our CRDT-based approach (eventual consistency with causal ordering via vector clocks).</p>
<p><strong>&quot;A Distributed Architecture for Multiplayer Interactive Applications on the Internet&quot;</strong> -- Diot and Gautier (1999). <a href="https://doi.org/10.1109/65.790947" target="_blank" rel="noreferrer">DOI</a>. Early research on distributed virtual environments that identified the core tension: strict consistency requires coordination (adding latency), while weak consistency allows responsiveness but risks visible inconsistency. The paper argues for &quot;local lag&quot; (delaying local display by a small amount to give time for remote updates to arrive) as a middle ground. For world edits (placing objects), a 100-200ms local lag is imperceptible and gives the server time to validate.</p>
<p><strong>&quot;The Second Life Grid: The Architecture of a Near-Contemporary Open Source Virtual World&quot;</strong> -- Linden Lab technical documentation and community reverse-engineering. While not a single paper, the technical analysis of Second Life's architecture is extensively documented. The key insights: each 256x256m region runs on a dedicated server instance. Objects are stored as a tree of &quot;primitives&quot; (basic shapes with transforms, textures, and scripts). The viewer streams object descriptions and textures on demand. This parcel-based model with per-object persistence is the closest existing architecture to what we're building, and Second Life's 20+ years of operation prove it scales.</p>
<h3 id="web-graphics-and-browser-performance" tabindex="-1">Web Graphics and Browser Performance <a class="header-anchor" href="#web-graphics-and-browser-performance" aria-label="Permalink to &quot;Web Graphics and Browser Performance&quot;"></a></h3>
<p><strong>&quot;WebGPU: A High-Performance Graphics API for the Web&quot;</strong> -- W3C GPU for the Web Working Group (2023-ongoing). <a href="https://www.w3.org/TR/webgpu/" target="_blank" rel="noreferrer">Specification</a>. The formal specification for WebGPU. Not a research paper, but the definitive technical document for browser GPU programming. The compute shader specification (Section 23) is particularly relevant for terrain generation, foliage scattering, and particle systems described throughout this guide.</p>
<p><strong>&quot;WebAssembly: A Framework for Running Compiled Code in the Browser&quot;</strong> -- Haas et al. (PLDI 2017). <a href="https://doi.org/10.1145/3062341.3062363" target="_blank" rel="noreferrer">DOI</a>. The original WebAssembly paper from the browser vendors. Demonstrates that Wasm achieves within 2x of native performance for compute-intensive workloads. This validates our recommendation of Wasm-compiled physics engines (Rapier, Havok) for browser open worlds. The paper's performance analysis shows that the overhead comes primarily from bounds checking and indirect function calls, not from the compilation model itself.</p>
<p><strong>&quot;Not So Fast: Analyzing the Performance of WebAssembly vs. Native Code&quot;</strong> -- Jangda et al. (USENIX ATC 2019). <a href="https://www.usenix.org/system/files/atc19-jangda.pdf" target="_blank" rel="noreferrer">PDF</a>. A rigorous benchmark of Wasm vs native performance. Finds that Wasm runs 1.45x-1.55x slower than native C on average across the SPEC CPU benchmark suite. For game physics specifically (floating-point heavy, few system calls), the overhead is at the lower end (~1.3x). This confirms that Wasm physics in a browser is viable for real-time game workloads.</p>
<p><strong>&quot;Bringing the Web up to Speed with WebAssembly&quot;</strong> -- Rossberg et al. (2018). <a href="https://doi.org/10.1145/3192366.3192379" target="_blank" rel="noreferrer">DOI</a>. Describes the design rationale and formal semantics of WebAssembly. Particularly relevant: the discussion of memory safety guarantees (Section 3) explains why Wasm modules can safely share a browser tab with JavaScript without the security risks of native plugins. This is what makes running physics engines (which were traditionally C++ libraries) safe in a browser.</p>
<h3 id="ai-powered-content-generation" tabindex="-1">AI-Powered Content Generation <a class="header-anchor" href="#ai-powered-content-generation" aria-label="Permalink to &quot;AI-Powered Content Generation&quot;"></a></h3>
<p><strong>&quot;Text-to-3D Generation with Bidirectional Diffusion Using Both 2D and 3D Priors&quot;</strong> -- Various groups (2023-2025). Multiple recent papers (DreamFusion, Magic3D, ProlificDreamer, MVDream, Zero-1-to-3++) explore generating 3D assets from text prompts by using 2D diffusion models as priors for 3D optimization. The quality has improved dramatically from early 2023 to 2025, going from blobby shapes to detailed, textured meshes. For our platform, these models (running server-side on GPUs) are the &quot;AI generation&quot; step in the creator asset pipeline.</p>
<p><strong>&quot;DreamFusion: Text-to-3D using 2D Diffusion&quot;</strong> -- Poole et al. (ICLR 2023). <a href="https://dreamfusion3d.github.io/" target="_blank" rel="noreferrer">Project page</a>. The foundational paper on Score Distillation Sampling (SDS), which uses a pre-trained 2D diffusion model to guide 3D optimization. The key insight: you don't need 3D training data if you can evaluate whether rendered views of a 3D object match a text prompt using an existing 2D model. This opened the door to text-to-3D generation and is the basis for subsequent work (Magic3D, ProlificDreamer) that improved quality and speed.</p>
<p><strong>&quot;LRM: Large Reconstruction Model for Single Image to 3D&quot;</strong> -- Hong et al. (ICLR 2024). <a href="https://yiconghong.me/LRM/" target="_blank" rel="noreferrer">Project page</a>. Reconstructs a 3D model from a single image in 5 seconds on a single GPU. The model outputs a NeRF-like representation that can be converted to a mesh. For a creator world, this means a creator could take a photo of any real-world object and get a 3D model within seconds. The speed makes it viable as an interactive tool rather than a batch process.</p>
<p><strong>&quot;Procedural Content Generation via Machine Learning (PCGML)&quot;</strong> -- Summerville et al. (2018). <a href="https://doi.org/10.1109/TG.2018.2846639" target="_blank" rel="noreferrer">DOI</a>. A survey of using machine learning for procedural content generation in games. Covers level generation, item generation, narrative generation, and world generation. Particularly relevant: the discussion of &quot;controllable generation&quot; where designers set high-level parameters and the ML model fills in details. This is the paradigm for AI-assisted world building: creators set intent (&quot;make this area a spooky forest&quot;), AI fills in the geometry, textures, and population.</p>
<h3 id="how-these-papers-connect-to-our-architecture" tabindex="-1">How These Papers Connect to Our Architecture <a class="header-anchor" href="#how-these-papers-connect-to-our-architecture" aria-label="Permalink to &quot;How These Papers Connect to Our Architecture&quot;"></a></h3>
<p>The research maps to our architecture in layers:</p>
<p><strong>Terrain pipeline:</strong> Perlin/simplex noise (Perlin 1985, 2001) generates the base heightmap. Hydraulic erosion (Mei et al. 2007) adds geological realism. Geometry clipmaps (Losasso and Hoppe 2004) or CDLOD (Strugar 2014) render the terrain efficiently in the browser. Atmospheric scattering (Bruneton and Neyret 2008) makes distant terrain look correct.</p>
<p><strong>Asset pipeline:</strong> Text-to-3D (DreamFusion et al.) and image-to-3D (LRM) generate assets server-side. Gaussian splatting (Kerbl et al. 2023) enables photogrammetry capture. Neuralangelo (Li et al. 2023) extracts clean meshes from neural captures. All outputs are processed into browser-ready GLB/KTX2.</p>
<p><strong>Rendering:</strong> SSAO (McGuire 2012) adds depth. Screen-space reflections (McGuire and Mara 2014) power water. FFT ocean (Tessendorf 2001) simulates water. Vertex animation textures (Dudash 2007) render crowds. FABRIK (Aristidou and Lasenby 2011) drives character IK.</p>
<p><strong>Networking:</strong> Interest management (Boulanger et al. 2006) filters updates by spatial relevance. Dead reckoning (Pantel and Wolf 2002) reduces bandwidth. CRDTs (Shapiro et al. 2011) handle collaborative editing. Client prediction with server reconciliation (Jefferson 1985, Valve Source Networking) provides responsiveness.</p>
<p><strong>World generation:</strong> WFC (Gumin 2016, Karth and Smith 2017) generates structures and layouts. PCGML (Summerville et al. 2018) provides the framework for AI-assisted generation where creators set intent and models fill details.</p>
<p>The research is mature. Most of these techniques have been used in shipped games for years. The innovation in bringing them to a browser isn't the algorithms. It's the engineering of making them work within browser memory, GPU, and network constraints, which is what the rest of this guide addresses.</p>
<h2 id="further-reading" tabindex="-1">Further Reading <a class="header-anchor" href="#further-reading" aria-label="Permalink to &quot;Further Reading&quot;"></a></h2>
<ul>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> covers WebGL, WebGPU, and WebAssembly fundamentals</li>
<li><a href="/guides/web-game-engines-comparison.html">Web Game Engines Comparison</a> compares engines for browser delivery</li>
<li><a href="/guides/threejs-usdc-tech-report.html">Three.js + USDC Tech Report</a> on loading 3D assets in the browser</li>
<li><a href="/guides/frontier-gen-ai-models.html">Frontier Open-Source Gen AI Models</a> for the AI generation pipeline</li>
<li><a href="/guides/co-op-game-design.html">Co-op Game Design</a> on multiplayer design patterns that keep players engaged</li>
<li><a href="/guides/landscape-generation-browser.html">Landscape Generation for Browser Open Worlds</a> — terrain, erosion, and vegetation rendering</li>
<li><a href="/tutorials/websocket-multiplayer-basics.html">WebSocket multiplayer basics</a> — getting started with browser multiplayer networking</li>
<li><a href="/tutorials/webgpu-getting-started.html">WebGPU getting started</a> — the rendering API for next-gen browser 3D</li>
<li><a href="/tutorials/streaming-asset-loading.html">Streaming asset loading</a> — progressive loading patterns for large 3D worlds</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Co-op Game Design in 2026]]></title>
            <link>https://app.cinevva.com/guides/co-op-game-design</link>
            <guid>https://app.cinevva.com/guides/co-op-game-design</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[What makes "friend slop" games work. Peak sold 11 million copies. Here's why.]]></description>
            <content:encoded><![CDATA[<h1 id="co-op-game-design-in-2026" tabindex="-1">Co-op Game Design in 2026 <a class="header-anchor" href="#co-op-game-design-in-2026" aria-label="Permalink to &quot;Co-op Game Design in 2026&quot;"></a></h1>
<p>There's a term floating around that developers either love or hate: &quot;friend slop.&quot;</p>
<p>It started as an insult. A way to dismiss low-budget co-op games as shallow content farms designed for viral clips. But then Peak sold 11 million copies in two months. R.E.P.O. dominated Steam. And suddenly the developers making these games started embracing the label.</p>
<p>&quot;We actually think friend slop is a fire term,&quot; said Paige Wilson from Aggro Crab, Peak's publisher. &quot;It's used to describe a low-cost game that you and your friends can pick up whenever, have some fun, hang out, and expect a bit of jank. I think some people might not view it as this, but we do!&quot;</p>
<p>Here's what I've learned about why these games work, and how you can design one.</p>
<h2 id="the-games-that-defined-2025" tabindex="-1">The Games That Defined 2025 <a class="header-anchor" href="#the-games-that-defined-2025" aria-label="Permalink to &quot;The Games That Defined 2025&quot;"></a></h2>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/Am4gM4b2UHw" title="Lethal Company Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p><strong>Lethal Company</strong> (2023) kicked off the wave. Solo developer Zeekerss created a co-op horror game where you collect scrap from abandoned facilities while avoiding monsters. It sold millions and spawned an entire genre.</p>
<p><a href="https://store.steampowered.com/app/1966720/Lethal_Company/" target="_blank" class="campaign-link">View Lethal Company on Steam →</a></p>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/oSfoK8eSeD8" title="R.E.P.O. Release Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p><strong>R.E.P.O.</strong> (2025) took the formula and made it goofier. Robot avatars. Exaggerated physics. Players swore it was &quot;so funny it was hard to be scared.&quot; It became one of the biggest co-op games of 2025. When Steam published its year-end best-sellers in December 2025, R.E.P.O. came out as the top-selling new game of the year by units, with Peak right behind it. Co-op friend slop games took the top slots outright, beating out big-budget releases that cost five times as much.</p>
<p><a href="https://store.steampowered.com/app/3241660/REPO/" target="_blank" class="campaign-link">View R.E.P.O. on Steam →</a></p>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/6BCdx7I1gz8" title="Peak Explained" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p><strong>Peak</strong> (2025) proved the formula works outside horror. Climb a mountain with friends. Fall off. Laugh. Try again. 11 million copies sold. The game was made in 4 weeks during a game jam in South Korea.</p>
<p>Peak kept adding free content after launch, rotating in a new Mesa biome in August 2025 and running a limited-time in-game concert with rapper bbno$ in December 2025. The low-budget jam game became a live service in everything but name.</p>
<p><a href="https://store.steampowered.com/app/3527290/PEAK/" target="_blank" class="campaign-link">View Peak on Steam →</a></p>
<h2 id="the-core-design-principles" tabindex="-1">The Core Design Principles <a class="header-anchor" href="#the-core-design-principles" aria-label="Permalink to &quot;The Core Design Principles&quot;"></a></h2>
<p>After studying these games and reading developer interviews, here's what they all share:</p>
<h3 id="_1-low-barrier-to-entry" tabindex="-1">1. Low Barrier to Entry <a class="header-anchor" href="#_1-low-barrier-to-entry" aria-label="Permalink to &quot;1. Low Barrier to Entry&quot;"></a></h3>
<div class="stat-box">
<strong>$8 or less</strong><br>
Peak's pricing theory: "Eight bucks is still five bucks. It doesn't become ten bucks."
</div>
<p>These games cost under $20. Most are under $10. Peak launched at $5, then $8. The developers had a whole theory about price psychology: &quot;Six bucks is still five bucks. Three bucks is two bucks. Two bucks is basically free.&quot;</p>
<p>But it's not just price. The games are easy to learn. You don't need prior gaming experience. You don't need to understand complex systems. Your non-gamer friends can join.</p>
<h3 id="_2-failure-is-funny" tabindex="-1">2. Failure Is Funny <a class="header-anchor" href="#_2-failure-is-funny" aria-label="Permalink to &quot;2. Failure Is Funny&quot;"></a></h3>
<p>In Peak, I've never survived to the end of a run. I keep playing anyway.</p>
<p>The most entertaining moments aren't victories. They're watching your friend miss a jump and fall off a mountain. Eat poisonous food. Get chased by the scoutmaster.</p>
<p>Design for comedic failure states. Make death spectatable. Let players laugh at each other.</p>
<h3 id="_3-proximity-chat" tabindex="-1">3. Proximity Chat <a class="header-anchor" href="#_3-proximity-chat" aria-label="Permalink to &quot;3. Proximity Chat&quot;"></a></h3>
<p>Almost every successful friend slop game has proximity voice chat. Your voice gets quieter as you move away from teammates. Character mouths lip-sync to what you're saying.</p>
<p>This creates natural comedy. Someone screaming for help in the distance. Whispered conversations. The panic when you realize you've wandered too far alone.</p>
<h3 id="_4-physics-jank-intentional" tabindex="-1">4. Physics Jank (Intentional) <a class="header-anchor" href="#_4-physics-jank-intentional" aria-label="Permalink to &quot;4. Physics Jank (Intentional)&quot;"></a></h3>
<p>The wobbly, unpredictable physics aren't bugs. They're features.</p>
<p>When your character ragdolls unexpectedly, it's funny. When objects bounce in weird ways, it creates stories. The jank is the content.</p>
<h3 id="_5-shareable-moments" tabindex="-1">5. Shareable Moments <a class="header-anchor" href="#_5-shareable-moments" aria-label="Permalink to &quot;5. Shareable Moments&quot;"></a></h3>
<p>These games are designed for clips. Twitch. TikTok. YouTube Shorts.</p>
<p>Every session produces moments worth sharing. The algorithm loves them. Players become marketers without realizing it.</p>
<h2 id="what-makes-them-different-from-traditional-co-op" tabindex="-1">What Makes Them Different From Traditional Co-op <a class="header-anchor" href="#what-makes-them-different-from-traditional-co-op" aria-label="Permalink to &quot;What Makes Them Different From Traditional Co-op&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Traditional Co-op</th>
<th>Friend Slop</th>
</tr>
</thead>
<tbody>
<tr>
<td>Winning is the goal</td>
<td>Hanging out is the goal</td>
</tr>
<tr>
<td>Skill required</td>
<td>Anyone can play</td>
</tr>
<tr>
<td>Solo mode works</td>
<td>Solo mode is sad</td>
</tr>
<tr>
<td>Story-driven</td>
<td>Moment-driven</td>
</tr>
<tr>
<td>$40-60</td>
<td>$5-20</td>
</tr>
<tr>
<td>Polished</td>
<td>Intentionally janky</td>
</tr>
</tbody>
</table>
<p>The Verge's analysis nailed it: &quot;In friend slop, the main objective is humor. Winning is secondary.&quot;</p>
<h2 id="the-loneliness-angle" tabindex="-1">The Loneliness Angle <a class="header-anchor" href="#the-loneliness-angle" aria-label="Permalink to &quot;The Loneliness Angle&quot;"></a></h2>
<p>Here's something the critics miss when they dismiss these games as &quot;slop.&quot;</p>
<p>We're in a loneliness epidemic. People are isolated. Remote work scattered friend groups. These games give people a reason to hang out virtually. They're low-commitment social spaces.</p>
<p>As one writer put it: &quot;a real path to salvation from divisive and isolating times.&quot;</p>
<p>That's not nothing.</p>
<h2 id="the-economics" tabindex="-1">The Economics <a class="header-anchor" href="#the-economics" aria-label="Permalink to &quot;The Economics&quot;"></a></h2>
<div class="stat-box">
<strong>4 weeks → 11 million copies</strong><br>
Peak was developed during a month-long game jam in South Korea.
</div>
<p>These games are cheap to make. Small teams. Short development cycles. Low-fidelity graphics that players actually prefer because they're charming.</p>
<p>Content Warning took a small team and minimal time. Peak was a game jam project. R.E.P.O. came from a studio that makes experimental games.</p>
<p>The studios behind these hits aren't slowing down. Aggro Crab, Peak's co-developer, shipped its next co-op game Crashout Crew on May 28, 2026, a chaotic forklift warehouse simulator for one to four players that launched on Steam and Xbox Game Pass. Same recipe: small team, physics-driven mayhem, designed to be played with friends.</p>
<p>The math works: low investment, high potential upside, and a genre that rewards authenticity over polish.</p>
<h2 id="notable-examples" tabindex="-1">Notable Examples <a class="header-anchor" href="#notable-examples" aria-label="Permalink to &quot;Notable Examples&quot;"></a></h2>
<p><strong>The Essentials:</strong></p>
<ul>
<li>Among Us (2018) - the prototype</li>
<li>Phasmophobia (2020) - ghost hunting with proximity chat</li>
<li>Lethal Company (2023) - the genre-definer</li>
<li>Content Warning (2024) - filming monsters for views</li>
<li>Peak (2025) - climbing chaos</li>
<li>R.E.P.O. (2025) - goofy horror extraction</li>
<li>Crashout Crew (2026) - forklift co-op mayhem from Peak's co-developer Aggro Crab</li>
<li>Gamble With Your Friends (2026) - party game that sold a million copies in its first week, proof the genre kept rolling into 2026</li>
</ul>
<p><strong>The Experiments:</strong></p>
<ul>
<li>Guilty as Sock! - courtroom drama with sock puppets</li>
<li>Mage Arena - $2.99, cast spells by shouting</li>
<li>Lockdown Protocol - Among Us meets chore simulator</li>
<li>YAPYAP - wizards vandalizing a tower</li>
</ul>
<h2 id="why-web-games-fit-perfectly" tabindex="-1">Why Web Games Fit Perfectly <a class="header-anchor" href="#why-web-games-fit-perfectly" aria-label="Permalink to &quot;Why Web Games Fit Perfectly&quot;"></a></h2>
<p>Here's where it gets interesting for browser-based developers.</p>
<p>Friend slop games have two requirements: low barrier to entry and instant multiplayer. Web games nail both.</p>
<p>No downloads. No installs. Share a link, everyone's playing in 30 seconds. That's the dream for co-op games.</p>
<p>The technical challenges (WebSocket multiplayer, physics sync, voice chat) are solvable. We have <a href="/tutorials/websocket-multiplayer-basics.html">WebSocket tutorials</a> for a reason.</p>
<p>The bigger question is design. Can you create moments worth sharing? Can you make failure funny? Can you give friends a reason to hang out in your game?</p>
<h2 id="if-you-re-building-one" tabindex="-1">If You're Building One <a class="header-anchor" href="#if-you-re-building-one" aria-label="Permalink to &quot;If You're Building One&quot;"></a></h2>
<p><strong>Start with the social loop.</strong> What will players talk about after? What stories will they tell? Design for those moments.</p>
<p><strong>Embrace the jank.</strong> You don't need AAA polish. Players prefer charm. Ragdolls are your friend.</p>
<p><strong>Price it low.</strong> $5-10 is the sweet spot. You want entire friend groups buying in. Four copies at $8 beats one copy at $30.</p>
<p><strong>Add proximity chat.</strong> It's practically required. Players expect it now.</p>
<p><strong>Make it clippable.</strong> If nobody's sharing clips of your game, you're missing free marketing.</p>
<p><strong>Test with actual friend groups.</strong> Solo playtesting won't tell you if the social dynamics work.</p>
<p>The games that win aren't the most polished. They're the ones that give friends a reason to laugh together.</p>
<hr>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="what-makes-a-good-co-op-game" tabindex="-1">What makes a good co-op game? <a class="header-anchor" href="#what-makes-a-good-co-op-game" aria-label="Permalink to &quot;What makes a good co-op game?&quot;"></a></h3>
<p>The best co-op games create situations where players need each other but also get in each other's way. You want moments of chaos that turn into stories people retell later. Low skill floors help because nobody wants to be the friend who can't figure out the controls. Funny failure states matter more than you'd think. When losing is entertaining, people keep playing. Proximity voice chat adds a layer that text chat can't replicate. And keep sessions short, 15 to 30 minutes, so people can fit a few rounds into an evening without it feeling like a commitment.</p>
<h3 id="why-are-co-op-games-so-popular-right-now" tabindex="-1">Why are co-op games so popular right now? <a class="header-anchor" href="#why-are-co-op-games-so-popular-right-now" aria-label="Permalink to &quot;Why are co-op games so popular right now?&quot;"></a></h3>
<p>A few things came together. Lethal Company proved in 2023 that a solo dev could build a co-op game and sell millions of copies. Then Peak hit 11 million copies in 2025 as a game that started as a jam project. The economics got hard to ignore. These games are cheap to make, they spread through friend groups like wildfire, and they generate clips that go viral on social media. The &quot;friend slop&quot; genre, as people call it, rewards authenticity over polish. You don't need a big studio or a big budget. You need funny moments and friends who want to share them.</p>
<h3 id="can-you-make-a-co-op-game-for-the-browser" tabindex="-1">Can you make a co-op game for the browser? <a class="header-anchor" href="#can-you-make-a-co-op-game-for-the-browser" aria-label="Permalink to &quot;Can you make a co-op game for the browser?&quot;"></a></h3>
<p>Absolutely. Browser games are actually a great fit for co-op because the biggest barrier to playing with friends is getting everyone set up. With a web game, you share a link and everyone's in. No downloads, no version mismatches, no &quot;hold on I need to update.&quot; The technical pieces, WebSocket multiplayer, physics sync, even basic voice chat through WebRTC, are all doable in the browser. We've got a <a href="/tutorials/websocket-multiplayer-basics.html">WebSocket multiplayer tutorial</a> that covers the networking side.</p>
<hr>
<p><strong>Related:</strong></p>
<ul>
<li><a href="/tutorials/websocket-multiplayer-basics.html">WebSocket Multiplayer Basics</a></li>
<li><a href="/guides/steam-next-fest-strategy.html">Steam Next Fest Strategy</a></li>
<li><a href="/guides/kickstarter-for-indie-games.html">Kickstarter for Indie Games</a></li>
<li><a href="/tutorials/gamepad-api.html">Gamepad API</a> — local co-op with multiple controllers</li>
<li><a href="/guides/game-jams-hackathons.html">Game Jams &amp; Hackathons</a> — co-op jam games get more ratings and shares</li>
<li><a href="/guides/itch-io-launch-guide.html">How to Launch Your Game on itch.io</a> — browser co-op games thrive on itch.io</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kickstarter for Indie Games in 2026]]></title>
            <link>https://app.cinevva.com/guides/kickstarter-for-indie-games</link>
            <guid>https://app.cinevva.com/guides/kickstarter-for-indie-games</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[What the data actually says, who's succeeding, who's failing, and whether crowdfunding makes sense for your game.]]></description>
            <content:encoded><![CDATA[<h1 id="kickstarter-for-indie-games-in-2026" tabindex="-1">Kickstarter for Indie Games in 2026 <a class="header-anchor" href="#kickstarter-for-indie-games-in-2026" aria-label="Permalink to &quot;Kickstarter for Indie Games in 2026&quot;"></a></h1>
<p>Let's talk about what's actually happening with game crowdfunding right now, because the vibes don't match the data.</p>
<p>The golden age is over. In 2013, video games raised $51 million on Kickstarter. By 2024, that dropped to $25.2 million across roughly the same number of campaigns. The pie shrunk by half, but the number of people fighting for slices barely changed. That's the math you're walking into.</p>
<p>And yet, developers keep turning to Kickstarter. Publishers are saying no. Investors got spooked by the layoff wave. Around 15,000 game industry jobs vanished in 2024 alone, and trackers note the real number is likely higher since not every cut gets reported. For a lot of teams, crowdfunding isn't Plan A anymore. It's the backup when everything else falls through.</p>
<h2 id="two-stories-from-2025" tabindex="-1">Two Stories From 2025 <a class="header-anchor" href="#two-stories-from-2025" aria-label="Permalink to &quot;Two Stories From 2025&quot;"></a></h2>
<h3 id="the-one-that-worked" tabindex="-1">The One That Worked <a class="header-anchor" href="#the-one-that-worked" aria-label="Permalink to &quot;The One That Worked&quot;"></a></h3>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/mrvpsBjg4nI" title="Elestrals Awakened Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p><strong>Elestrals Awakened</strong> raised over $1.3 million from more than 9,000 backers, making it the most funded video game on Kickstarter in 2025. The team behind it? A former Pokemon Championship caster with nearly a million YouTube subscribers, and two successful Kickstarters already under their belt (a trading card game and its digital version). They had an audience. They had a track record. They had years of community-building before they ever asked for money.</p>
<p><a href="https://www.kickstarter.com/projects/elestrals/elestrals-awakened" target="_blank" class="campaign-link">View the Elestrals Awakened Kickstarter →</a></p>
<h3 id="the-one-that-didn-t" tabindex="-1">The One That Didn't <a class="header-anchor" href="#the-one-that-didn-t" aria-label="Permalink to &quot;The One That Didn't&quot;"></a></h3>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/dAzaaBUqki4" title="Alzara Radiant Echoes Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p><strong>Alzara: Radiant Echoes</strong> raised €300,000 from over 5,000 backers in 2024. One of the most-backed video game campaigns that year. The studio, Camelia, shut down in April 2025. No game. No refunds. The team had a playable demo, a senior team, half their funding secured, and the Kickstarter success as proof of market interest. They pitched at Tokyo Game Show, DICE, and every industry event they could find. Nobody bit.</p>
<blockquote>
<p>&quot;This is part of the harsh reality of the industry. Sometimes, even having a promising game concept with proven market interest and a strong team is not enough.&quot;</p>
<p>— Studio Camelia, final update to backers</p>
</blockquote>
<p><a href="https://www.kickstarter.com/projects/studiocamelia/seed-a-vibrant-tribute-to-jrpg-classics" target="_blank" class="campaign-link">View the Alzara Kickstarter (archived) →</a></p>
<p>Two campaigns. Both &quot;successful&quot; by Kickstarter's definition. One delivered nothing. The difference wasn't the money raised. It was everything that came before.</p>
<h2 id="the-numbers-that-matter" tabindex="-1">The Numbers That Matter <a class="header-anchor" href="#the-numbers-that-matter" aria-label="Permalink to &quot;The Numbers That Matter&quot;"></a></h2>
<p>Data from 17,000 Kickstarter campaigns since 2009 tells a clear story:</p>
<p><strong>Goal size predicts success.</strong> None of the 15 campaigns aiming for $2.5 million or more succeeded. But games with goals under $500K often overperformed. Shenmue 3 asked for $2M and got $6.3M. Bloodstained asked for $500K and got $5.5M. Set a goal people can rally behind, then blow past it.</p>
<p><strong>Timing matters.</strong> December is the worst month to launch (28% success rate). February is the best (35%). The overall average is 32%. Yes, two-thirds of campaigns fail.</p>
<p><strong>Most money comes from modest campaigns.</strong> 81.8% of all funds raised came from campaigns with goals of $500K or less. 68.1% from campaigns under $250K. The mega-campaigns get the headlines, but the mid-size ones actually fund.</p>
<p>The 2025 numbers are now in, and they tell a more nuanced story than the long decline. Video games drew 443 successful campaigns in 2025, raising just shy of 2024's roughly $26 million. The interesting shift was at the top: 11 campaigns cleared $500,000, the most in that band since 2015 and more than double the count in 2024. The pool isn't growing, but the big, well-prepared campaigns are coming back.</p>
<h2 id="what-50-000-actually-buys-you" tabindex="-1">What $50,000 Actually Buys You <a class="header-anchor" href="#what-50-000-actually-buys-you" aria-label="Permalink to &quot;What $50,000 Actually Buys You&quot;"></a></h2>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/BoqbkWJqlVY" title="Space Chef Kickstarter Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>Here's a postmortem from the <strong>Space Chef</strong> team, who raised $50,000 from 1,119 backers:</p>
<blockquote>
<p>&quot;To get $50,000, we had to spend $20,000+ on marketing, ads, and creators. The trailer took 3 months to make. Kickstarter isn't free money. It's a multi-year commitment to hundreds of people.&quot;</p>
</blockquote>
<p>Their math:</p>
<table tabindex="0">
<thead>
<tr>
<th>Item</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>Raised</td>
<td>$50,000</td>
</tr>
<tr>
<td>Marketing</td>
<td>-$20,000</td>
</tr>
<tr>
<td>Fees &amp; taxes</td>
<td>-$4,000</td>
</tr>
<tr>
<td><strong>Left for development</strong></td>
<td><strong>$26,000</strong></td>
</tr>
</tbody>
</table>
<p>At $20/hour, that's 1,300 hours. About 32 weeks. Nowhere near enough to finish their game.</p>
<p>They eventually got publisher investment to complete it. The Kickstarter didn't fund the game. It proved the game was worth funding.</p>
<p><a href="https://www.kickstarter.com/projects/bluegoogames/space-chef" target="_blank" class="campaign-link">View the Space Chef Kickstarter →</a></p>
<h2 id="when-it-makes-sense" tabindex="-1">When It Makes Sense <a class="header-anchor" href="#when-it-makes-sense" aria-label="Permalink to &quot;When It Makes Sense&quot;"></a></h2>
<p>Kickstarter works when you're using it for validation, not salvation.</p>
<p><strong>Good reasons to run a campaign:</strong></p>
<ul>
<li>You have an audience that's already asking for your game</li>
<li>You need proof of market demand to attract a publisher</li>
<li>You want to build a community that's literally invested in your success</li>
<li>You have a track record (previous games, successful projects, visible expertise)</li>
</ul>
<p><strong>Bad reasons:</strong></p>
<ul>
<li>You need the money to start development</li>
<li>You have no existing audience</li>
<li>You're hoping the campaign itself will build awareness</li>
<li>You can't afford to fail</li>
</ul>
<h2 id="the-brutal-truths" tabindex="-1">The Brutal Truths <a class="header-anchor" href="#the-brutal-truths" aria-label="Permalink to &quot;The Brutal Truths&quot;"></a></h2>
<p><strong>You probably can't finish your game on Kickstarter money alone.</strong> Most successful campaigns cover 25-50% of actual development costs. The rest comes from publishers, grants, savings, or day jobs.</p>
<p><strong>Your backers can't get refunds if you fail.</strong> They know this. They're increasingly skeptical of first-time creators. Studios with shipped games or proven track records get more trust.</p>
<p><strong>Running a campaign is a full-time job.</strong> Updates every few days. Backer messages. Press outreach. Stretch goal planning. Social media. For 30+ days, you're not making your game. You're marketing it.</p>
<p><strong>Success rate is 32%.</strong> That means 68% of campaigns fail. If you launch without preparation, you're almost certainly in that 68%.</p>
<h2 id="if-you-re-still-in" tabindex="-1">If You're Still In <a class="header-anchor" href="#if-you-re-still-in" aria-label="Permalink to &quot;If You're Still In&quot;"></a></h2>
<p>Do this before you launch:</p>
<ol>
<li>
<p><strong>Build your audience first.</strong> 1,000-2,000 people who actually want your game. Discord, email list, social following. Whatever works.</p>
</li>
<li>
<p><strong>Have something playable.</strong> A demo, a vertical slice, something. Players have been burned too many times. Showing beats promising.</p>
</li>
<li>
<p><strong>Set a goal you can actually hit.</strong> Ask for what you need to finish, not what you'd like to have. Fund fast, then stretch.</p>
</li>
<li>
<p><strong>Plan for the work after.</strong> Updates. Fulfillment. Years of development with people watching. This isn't a transaction. It's a relationship.</p>
</li>
</ol>
<p>The teams that succeed treat Kickstarter as the start of a conversation, not the end of a fundraising problem. Everyone else treats it like a lottery ticket.</p>
<p>Don't be a lottery ticket.</p>
<hr>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="is-kickstarter-still-worth-it-for-indie-games" tabindex="-1">Is Kickstarter still worth it for indie games? <a class="header-anchor" href="#is-kickstarter-still-worth-it-for-indie-games" aria-label="Permalink to &quot;Is Kickstarter still worth it for indie games?&quot;"></a></h3>
<p>It depends on what you're bringing to the table. If you've got an audience, a playable demo, and realistic expectations, it can work. The success rate for video game campaigns sits at about 32%, which isn't great odds. But teams with existing communities and prior track records do much better than that average. The real question is whether you've done the groundwork. If you're launching cold with no following, your chances are rough.</p>
<h3 id="how-much-money-do-indie-games-raise-on-kickstarter" tabindex="-1">How much money do indie games raise on Kickstarter? <a class="header-anchor" href="#how-much-money-do-indie-games-raise-on-kickstarter" aria-label="Permalink to &quot;How much money do indie games raise on Kickstarter?&quot;"></a></h3>
<p>The total pool for video games on Kickstarter has been shrinking. It peaked around $51 million back in 2013 and dropped to about $25.2 million in 2024. Most successful campaigns raise between $10,000 and $50,000. The thing people forget is how much of that disappears. Kickstarter takes about 5%, payment processing takes another 3-5%, then you've got reward fulfillment, shipping, and taxes. A campaign that raises $50,000 might leave you with $26,000 in actual working money.</p>
<h3 id="what-percentage-of-game-kickstarters-fail" tabindex="-1">What percentage of game Kickstarters fail? <a class="header-anchor" href="#what-percentage-of-game-kickstarters-fail" aria-label="Permalink to &quot;What percentage of game Kickstarters fail?&quot;"></a></h3>
<p>About 68% of video game campaigns on Kickstarter don't reach their funding goal. That number has stayed pretty consistent over the years. The campaigns that fail usually share a few traits: no existing audience, no playable demo, unrealistic funding goals, or launching during a crowded period without any way to stand out.</p>
<hr>
<p><strong>Related:</strong></p>
<ul>
<li><a href="/guides/itch-io-launch-guide.html">How to Launch Your Game on itch.io</a></li>
<li><a href="/guides/game-jams-hackathons.html">Game Jams &amp; Hackathons Guide</a></li>
<li><a href="/guides/steam-next-fest-strategy.html">Steam Next Fest Strategy</a> — building wishlists before and alongside crowdfunding</li>
<li><a href="/guides/co-op-game-design.html">Co-op Game Design</a> — co-op games generate organic social sharing that helps campaigns</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Steam Next Fest Strategy in 2026]]></title>
            <link>https://app.cinevva.com/guides/steam-next-fest-strategy</link>
            <guid>https://app.cinevva.com/guides/steam-next-fest-strategy</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[What the data says about Steam Next Fest wishlists, demos, and timing, with real numbers from 200+ games and what actually drives visibility.]]></description>
            <content:encoded><![CDATA[<h1 id="steam-next-fest-strategy-in-2026" tabindex="-1">Steam Next Fest Strategy in 2026 <a class="header-anchor" href="#steam-next-fest-strategy-in-2026" aria-label="Permalink to &quot;Steam Next Fest Strategy in 2026&quot;"></a></h1>
<p><em>Last updated: June 2026.</em></p>
<p>Here's the thing about Next Fest: everyone treats it like a lottery. Throw your demo in, cross your fingers, hope the algorithm smiles on you. That's not how it works.</p>
<p>I've been reading postmortems obsessively. Hundreds of them from 2025 alone. The pattern is so consistent it's almost boring: the games that &quot;win&quot; Next Fest aren't discovering success during the festival. They're amplifying momentum they already built.</p>
<p>Let me show you what I mean.</p>
<h2 id="the-formula-nobody-wants-to-hear" tabindex="-1">The Formula Nobody Wants to Hear <a class="header-anchor" href="#the-formula-nobody-wants-to-hear" aria-label="Permalink to &quot;The Formula Nobody Wants to Hear&quot;"></a></h2>
<div class="stat-box">
<strong>Wishlists Before × 2 = Wishlists Gained</strong><br>
This ratio shows up in postmortem after postmortem.
</div>
<p>Eilean Mor went into October 2025 with 2,117 wishlists. Gained 2,082 during the festival. Almost exactly 2×.</p>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/jFv7SjsQEws" title="Eilean Mor: The Lost Keepers Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>Dice of Kalma went in with 335 wishlists. Gained 659. Just under 2×. Their developer wrote: &quot;Our wishlists nearly tripled, which is awesome, but we have still lot of work to do.&quot;</p>
<p>That's the uncomfortable truth. Next Fest doubles what you already have. If you have nothing, you get nothing doubled.</p>
<p><a href="https://store.steampowered.com/app/3871570/Eilean_Mor_The_Lost_Keepers/" target="_blank" class="campaign-link">View Eilean Mor on Steam →</a></p>
<h2 id="the-2-000-wishlist-cliff" tabindex="-1">The 2,000 Wishlist Cliff <a class="header-anchor" href="#the-2-000-wishlist-cliff" aria-label="Permalink to &quot;The 2,000 Wishlist Cliff&quot;"></a></h2>
<p>Data from 208 games in February 2025 shows a sharp inflection point at 2,000 wishlists.</p>
<table tabindex="0">
<thead>
<tr>
<th>Starting Wishlists</th>
<th>What Happens</th>
</tr>
</thead>
<tbody>
<tr>
<td>Under 1,000</td>
<td>You're invisible after day 2. Zero games jumped tiers.</td>
</tr>
<tr>
<td>1,000–2,000</td>
<td>Modest gains. You stay in your lane.</td>
</tr>
<tr>
<td>2,000+</td>
<td>The algorithm starts helping you. Real momentum possible.</td>
</tr>
<tr>
<td>10,000+</td>
<td>Gold tier. Strong gains likely.</td>
</tr>
<tr>
<td>100,000+</td>
<td>Diamond tier. You're competing for the top charts.</td>
</tr>
</tbody>
</table>
<p>Here's what a developer from that survey said: &quot;You really start to see how the wishlists a game earns takes off for games that have 2000+ wishlists before Steam Next Fest.&quot;</p>
<p>It's not an algorithm thing exactly. It's a validation thing. 2,000 wishlists means you've figured out something about marketing. You know how to reach people. That skill compounds during the festival.</p>
<h2 id="what-february-2026-confirmed" tabindex="-1">What February 2026 Confirmed <a class="header-anchor" href="#what-february-2026-confirmed" aria-label="Permalink to &quot;What February 2026 Confirmed&quot;"></a></h2>
<p>The February 2026 Next Fest (February 23 to March 2) was the most crowded yet, with over 3,500 demos. That's a 51% jump over February 2025. And the bigger the field gets, the louder the data screams the same thing.</p>
<p>How To Market A Game surveyed 182 developers afterward. The single strongest correlation in the whole dataset was between a game's pre-fest wishlist count and the wishlists it earned during the event: a Spearman r of 0.825. For comparison, most marketing correlations in indie data sit between 0.2 and 0.4. Nothing else came close.</p>
<p>The spread was brutal. The median demo gained 806 wishlists. The 70th percentile hit 1,839. You needed 13,461 to crack the 95th percentile, and the top earner pulled 57,074. A cold-launched demo with no follower base had roughly a 1 in 20 shot at reaching that top 5%. The festival rewards momentum you bring with you. It does not manufacture it.</p>
<p>The timing data backed up the early-demo rule too. Of the games that gained 15,000+ wishlists, five had demos out months ahead, four launched about a month before, and only one shadow-dropped days before the fest.</p>
<h2 id="the-first-48-hours-are-everything" tabindex="-1">The First 48 Hours Are Everything <a class="header-anchor" href="#the-first-48-hours-are-everything" aria-label="Permalink to &quot;The First 48 Hours Are Everything&quot;"></a></h2>
<p>Valve gives everyone a shot on days 1 and 2. After that, the algorithm decides who deserves more visibility based on performance.</p>
<p>Bronze tier games? Their impressions fall off a cliff on day 3. Diamond tier games? Their impressions triple.</p>
<div class="stat-box">
<strong>Day 1-2:</strong> Push everything. Stream. Post. Coordinate with creators.<br>
<strong>Day 3+:</strong> The algorithm has already decided. You're along for the ride.
</div>
<h2 id="what-actually-won-in-2025" tabindex="-1">What Actually Won in 2025 <a class="header-anchor" href="#what-actually-won-in-2025" aria-label="Permalink to &quot;What Actually Won in 2025&quot;"></a></h2>
<h3 id="do-no-harm-february-2025" tabindex="-1">Do No Harm (February 2025) <a class="header-anchor" href="#do-no-harm-february-2025" aria-label="Permalink to &quot;Do No Harm (February 2025)&quot;"></a></h3>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/GBlOHAL1LkY" title="Do No Harm Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p><strong>The numbers:</strong> 45,655 wishlists gained during Next Fest. Started with 52,102.</p>
<p><strong>What they did:</strong></p>
<ul>
<li>Trailer dropped on GameTrailers January 29 (weeks before Next Fest)</li>
<li>PR outreach timed with trailer</li>
<li>Demo went live February 4</li>
<li>Paid ads to amplify organic momentum</li>
</ul>
<p>This is the playbook. They didn't rely on Next Fest to discover them. They used Next Fest to multiply attention they'd already earned.</p>
<p><a href="https://store.steampowered.com/app/3138780/Do_No_Harm/" target="_blank" class="campaign-link">View Do No Harm on Steam →</a></p>
<h3 id="cairn-october-2025" tabindex="-1">Cairn (October 2025) <a class="header-anchor" href="#cairn-october-2025" aria-label="Permalink to &quot;Cairn (October 2025)&quot;"></a></h3>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/x6KWhvvk3bM" title="Cairn Launch Date Trailer" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>Hit #5 demo on day 1. Trending Upcoming and Top Demos by day 2.</p>
<p><strong>What they did:</strong></p>
<ul>
<li>Demo launched December 2024 (10 months before their Next Fest)</li>
<li>32,000 followers before the festival</li>
<li>Accepted into 18 festivals before Next Fest</li>
<li>The Game Bakers (Furi, Haven) - established studio with track record</li>
</ul>
<p>They didn't need Next Fest. But they used it anyway because it's free amplification when you've already done the work.</p>
<p><a href="https://store.steampowered.com/app/1588550/Cairn/" target="_blank" class="campaign-link">View Cairn on Steam →</a></p>
<h3 id="yapyap-october-2025" tabindex="-1">YAPYAP (October 2025) <a class="header-anchor" href="#yapyap-october-2025" aria-label="Permalink to &quot;YAPYAP (October 2025)&quot;"></a></h3>
<div class="video-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/7d8Fd9XXAtQ" title="YAPYAP Demo Gameplay" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<p>The &quot;friend slop&quot; phenomenon. Co-op horror where you play as wizards vandalizing a tower. Went viral on social media, then dominated Next Fest.</p>
<p>Their trailer dropped August 25, 2025 - one month before Next Fest. It earned 434K views. That was the inflection point. Everything after was just riding the wave.</p>
<p><a href="https://store.steampowered.com/app/3834090/YAPYAP/" target="_blank" class="campaign-link">View YAPYAP on Steam →</a></p>
<h2 id="the-genres-that-keep-winning" tabindex="-1">The Genres That Keep Winning <a class="header-anchor" href="#the-genres-that-keep-winning" aria-label="Permalink to &quot;The Genres That Keep Winning&quot;"></a></h2>
<p>From the February 2025 diamond tier:</p>
<ul>
<li><strong>Horror</strong> (×2) - Steam loves horror. Always has.</li>
<li><strong>Strategy/Management</strong> (×4) - &quot;Crafty-buildy-simulationy&quot; is the vibe</li>
<li><strong>Co-op &quot;friend slop&quot;</strong> - Lethal Company opened the floodgates</li>
<li><strong>Point-and-click</strong> (×2) - but both were sequels with existing audiences</li>
</ul>
<p>The point-and-click thing is interesting. Beholder: Conductor was the 4th game in its series. Duck Detective was a sequel. They had built-in audiences. First-time point-and-click devs don't see those results.</p>
<h2 id="what-the-small-games-say" tabindex="-1">What The Small Games Say <a class="header-anchor" href="#what-the-small-games-say" aria-label="Permalink to &quot;What The Small Games Say&quot;"></a></h2>
<p>Not everyone is chasing diamond tier. Here's what developers at the smaller end reported from October 2025:</p>
<blockquote>
<p>&quot;62% conversion rate from visits to wishlists. Store page itself seems to work. But impressions were high and people didn't click. Capsule art needs an update.&quot;</p>
</blockquote>
<blockquote>
<p>&quot;57% of store visitors played our demo. That sounds high? But only 15% who played also wishlisted. Something didn't meet expectations.&quot;</p>
</blockquote>
<blockquote>
<p>&quot;Got permanently banned from r/cozygames for asking if they found our game cozy. Always try to follow the rules but this was pretty surprising.&quot;</p>
</blockquote>
<p>The grind is real. These developers are testing capsule art, tweaking tags, getting banned from subreddits. It's not glamorous.</p>
<h2 id="the-honest-playbook" tabindex="-1">The Honest Playbook <a class="header-anchor" href="#the-honest-playbook" aria-label="Permalink to &quot;The Honest Playbook&quot;"></a></h2>
<p><strong>Months before Next Fest:</strong></p>
<ol>
<li>Launch your demo early. Like, way early. The winners had demos out for 6-12 months.</li>
<li>Enter smaller genre festivals first. Use them as polish runs.</li>
<li>Build to 2,000+ wishlists. That's your real threshold.</li>
</ol>
<p><strong>During Next Fest:</strong></p>
<ol>
<li>Days 1-2 are make or break. Push everything.</li>
<li>Stream if you can. It shows as &quot;live&quot; on your page.</li>
<li>Coordinate with streamers. Pay them if you have to.</li>
<li>Put your demo on a separate Steam page so it can collect reviews.</li>
</ol>
<p><strong>After Next Fest:</strong></p>
<ol>
<li>Don't launch immediately. A few weeks later actually performs better.</li>
<li>Use the momentum for your actual launch marketing.</li>
</ol>
<h2 id="when-to-skip-it" tabindex="-1">When To Skip It <a class="header-anchor" href="#when-to-skip-it" aria-label="Permalink to &quot;When To Skip It&quot;"></a></h2>
<p>Real talk: Next Fest isn't for everyone.</p>
<p>Skip it if:</p>
<ul>
<li>You have under 1,000 wishlists (you'll be invisible by day 3)</li>
<li>Your demo isn't polished (you get one shot at first impressions)</li>
<li>It's your first festival ever (do smaller ones first)</li>
<li>You're launching immediately after (wait a few weeks)</li>
</ul>
<p>There are dozens of smaller festivals every month. More focused audiences. Less competition. Check <a href="https://howtomarketagame.com/festivals" target="_blank" rel="noreferrer">howtomarketagame.com/festivals</a> for the list.</p>
<p>Next Fest is the default because Valve prompts everyone to join. But the best marketing isn't the stuff everyone gets prompted to do.</p>
<hr>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="how-many-wishlists-do-you-need-for-steam-next-fest" tabindex="-1">How many wishlists do you need for Steam Next Fest? <a class="header-anchor" href="#how-many-wishlists-do-you-need-for-steam-next-fest" aria-label="Permalink to &quot;How many wishlists do you need for Steam Next Fest?&quot;"></a></h3>
<p>The data points to about 2,000 wishlists as the threshold where Next Fest starts working for you. Below that, you're basically invisible by day three. The algorithm favors games that are already getting traction, so you need momentum going in. The rough pattern from the February 2025 festival: whatever your wishlist count was before Next Fest, you can roughly double it during the event. So if you go in with 500, you might come out with 1,000. Go in with 5,000, you could hit 10,000. The starting number matters way more than most people realize.</p>
<h3 id="when-should-you-release-a-demo-for-steam-next-fest" tabindex="-1">When should you release a demo for Steam Next Fest? <a class="header-anchor" href="#when-should-you-release-a-demo-for-steam-next-fest" aria-label="Permalink to &quot;When should you release a demo for Steam Next Fest?&quot;"></a></h3>
<p>Earlier than you'd think. The games that performed best in recent festivals had demos out for 6 to 12 months before Next Fest even started. That gives you time to collect feedback, fix the obvious stuff, and build wishlists organically before the festival multiplies whatever you've already got. Dropping your demo the week of Next Fest is a common mistake. You're competing with hundreds of other new demos and you've got no reviews, no feedback loop, nothing to work with.</p>
<h3 id="is-steam-next-fest-worth-it-for-small-indie-games" tabindex="-1">Is Steam Next Fest worth it for small indie games? <a class="header-anchor" href="#is-steam-next-fest-worth-it-for-small-indie-games" aria-label="Permalink to &quot;Is Steam Next Fest worth it for small indie games?&quot;"></a></h3>
<p>It can be, but only if you're prepared. Small games that go in with under 1,000 wishlists and an unpolished demo usually get buried fast. The festival runs for a week and there are hundreds of games competing for attention. If you're early in development, smaller genre-specific festivals are a better first step. They have more focused audiences and less competition. Save Next Fest for when your demo is tight and you've got some momentum behind you.</p>
<hr>
<p><strong>Related:</strong></p>
<ul>
<li><a href="/guides/kickstarter-for-indie-games.html">Kickstarter for Indie Games</a></li>
<li><a href="/guides/itch-io-launch-guide.html">How to Launch Your Game on itch.io</a></li>
<li><a href="/guides/co-op-game-design.html">Co-op Game Design</a> — co-op games consistently overperform at Next Fest</li>
<li><a href="/tutorials/analytics-web-games.html">Analytics for web games</a> — tracking demo performance and conversion metrics</li>
<li><a href="/guides/game-jams-hackathons.html">Game Jams &amp; Hackathons</a> — building audience before your first festival</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Best Open-Source Generative AI Models for Games (2026)]]></title>
            <link>https://app.cinevva.com/guides/frontier-gen-ai-models</link>
            <guid>https://app.cinevva.com/guides/frontier-gen-ai-models</guid>
            <pubDate>Sat, 24 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The best open-source generative AI models in 2026 for video, image, 3D, audio, speech, and world simulation, with what each one is good for and what it costs to run.]]></description>
            <content:encoded><![CDATA[<h1 id="best-open-source-generative-ai-models-for-games-2026" tabindex="-1">Best Open-Source Generative AI Models for Games (2026) <a class="header-anchor" href="#best-open-source-generative-ai-models-for-games-2026" aria-label="Permalink to &quot;Best Open-Source Generative AI Models for Games (2026)&quot;"></a></h1>
<p><em>Last updated: June 2026.</em></p>
<p>Here's the thing about generative AI — for years, the best models lived behind API keys and unpredictable pricing. You'd build your workflow around a tool, get comfortable, then wake up to an email saying the pricing changed. Or worse, the company pivoted entirely.</p>
<p>That changed in late 2024. Tencent, Alibaba, DeepSeek — they started releasing models you can actually download. Models that rival the closed alternatives. And suddenly, creators have options that don't depend on someone else's business model.</p>
<p>What if you could generate video, 3D assets, music, and voices — all from models you control? That's where we are now. This guide walks through what's real, what works, and what you can start using today.</p>
<h2 id="video-generation" tabindex="-1">Video Generation <a class="header-anchor" href="#video-generation" aria-label="Permalink to &quot;Video Generation&quot;"></a></h2>
<p>For years, video generation meant Runway or Pika — closed platforms, subscription fees, limits on what you could do with the output. Now? You can run comparable models on your own hardware.</p>
<video controls autoplay loop muted playsinline style="width: 100%; border-radius: 8px;">
  <source src="https://cdn.cinevva.com/video/hunyuanvideo-demo.mp4" type="video/mp4">
</video>
<p><em>HunyuanVideo text-to-video generation — 720p output from the leading open-source video model</em></p>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Params</th>
<th>Specs</th>
<th>Hardware</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/Tencent-Hunyuan/HunyuanVideo" target="_blank" rel="noreferrer"><strong>HunyuanVideo</strong></a></td>
<td>Tencent</td>
<td>13B</td>
<td>720p, text+img</td>
<td>80GB</td>
<td>~$0.20</td>
</tr>
<tr>
<td><a href="https://github.com/Tencent-Hunyuan/HunyuanVideo-1.5" target="_blank" rel="noreferrer"><strong>HunyuanVideo-1.5</strong></a></td>
<td>Tencent</td>
<td>8.3B</td>
<td>480p-1080p, text+img</td>
<td>14GB</td>
<td>~$0.05</td>
</tr>
<tr>
<td><a href="https://github.com/genmoai/mochi" target="_blank" rel="noreferrer"><strong>Mochi 1</strong></a></td>
<td>Genmo</td>
<td>10B</td>
<td>480p@30fps</td>
<td>12GB+</td>
<td>~$0.10</td>
</tr>
<tr>
<td><a href="https://github.com/Lightricks/LTX-Video" target="_blank" rel="noreferrer"><strong>LTX-Video</strong></a></td>
<td>Lightricks</td>
<td>—</td>
<td>768x512, real-time</td>
<td>12GB</td>
<td>~$0.02</td>
</tr>
<tr>
<td><a href="https://huggingface.co/Lightricks/LTX-2" target="_blank" rel="noreferrer"><strong>LTX-2</strong></a></td>
<td>Lightricks</td>
<td>19B</td>
<td>4K, <strong>synced audio</strong></td>
<td>High-end</td>
<td>~$0.30</td>
</tr>
<tr>
<td><a href="https://github.com/Wan-Video/Wan2.1" target="_blank" rel="noreferrer"><strong>Wan 2.1</strong></a></td>
<td>Alibaba</td>
<td>1.3-14B</td>
<td>480p-720p</td>
<td>8GB+</td>
<td>~$0.03</td>
</tr>
<tr>
<td><a href="https://github.com/Wan-Video/Wan2.2" target="_blank" rel="noreferrer"><strong>Wan 2.2</strong></a></td>
<td>Alibaba</td>
<td>27B MoE (14B active)</td>
<td>720p, MoE</td>
<td>8GB+</td>
<td>~$0.03</td>
</tr>
<tr>
<td><a href="https://huggingface.co/zai-org/CogVideoX-5b" target="_blank" rel="noreferrer"><strong>CogVideoX</strong></a></td>
<td>Tsinghua</td>
<td>5B</td>
<td>720x480@8fps</td>
<td>12GB</td>
<td>~$0.04</td>
</tr>
<tr>
<td><a href="https://github.com/hpcaitech/Open-Sora" target="_blank" rel="noreferrer"><strong>Open-Sora 2.0</strong></a></td>
<td>HPC-AI</td>
<td>11B</td>
<td>Flux integration</td>
<td>High-end</td>
<td>~$0.20</td>
</tr>
</tbody>
</table>
<p>Weights: <a href="https://huggingface.co/tencent/HunyuanVideo" target="_blank" rel="noreferrer">HunyuanVideo ↗</a> · <a href="https://huggingface.co/genmo/mochi-1-preview" target="_blank" rel="noreferrer">Mochi 1 ↗</a> · <a href="https://huggingface.co/Wan-AI/Wan2.1-T2V-14B" target="_blank" rel="noreferrer">Wan 2.1 ↗</a> · <a href="https://huggingface.co/hpcai-tech/Open-Sora-v2" target="_blank" rel="noreferrer">Open-Sora ↗</a></p>
<p><strong>See samples:</strong> <a href="https://aivideo.hunyuan.tencent.com/" target="_blank" rel="noreferrer">HunyuanVideo gallery ↗</a> · <a href="https://www.genmo.ai/" target="_blank" rel="noreferrer">Mochi examples ↗</a> · <a href="https://github.com/THUDM/CogVideo#gallery" target="_blank" rel="noreferrer">CogVideoX samples ↗</a></p>
<h3 id="what-this-means-for-creators" tabindex="-1">What this means for creators <a class="header-anchor" href="#what-this-means-for-creators" aria-label="Permalink to &quot;What this means for creators&quot;"></a></h3>
<p><strong>HunyuanVideo</strong> outperforms Runway Gen-3 in professional evaluations, and it's fully open. The catch? You need serious hardware. An A100 or H100 with 80GB VRAM. For most of us, that means renting cloud GPUs when you need them.</p>
<p><strong>HunyuanVideo-1.5</strong> changed the calculus on hardware. Tencent released it in November 2025 as an 8.3B-parameter open model that runs on a 14GB consumer GPU, with text-to-video and image-to-video at 480p/720p and optional 1080p upscaling. A new Selective and Sliding Tile Attention (SSTA) mechanism gives it roughly double the inference speed of the original HunyuanVideo. If you wanted HunyuanVideo quality but couldn't justify an 80GB card, this is the version you can actually run at home.</p>
<p><strong>Mochi 1</strong> is the one you can actually run. A 12GB GPU — that's RTX 3060 territory — handles it fine. The output is genuinely creative, with a distinct artistic quality. Not quite HunyuanVideo's fidelity, but you own the process.</p>
<p><strong>LTX-2</strong> is where things get interesting for games. It's the first open model that generates synchronized audio with video. Imagine cutscenes where the sound just... matches. No post-production sync. Lightricks open-sourced the full weights, inference, and training code in January 2026, with native 4K output at up to 50fps and synced audio up to 20 seconds.</p>
<p><strong>Wan 2.1</strong> runs on a gaming laptop. An 8GB GPU works for the smaller variants. If you've ever wanted to prototype with video generation but couldn't justify the hardware, this is your path in.</p>
<p><strong>Wan 2.2</strong> (Alibaba, July 2025) is the first open-source video model built on a Mixture-of-Experts design: 27B total parameters with only 14B active per step. It ships as text-to-video (T2V-A14B), image-to-video (I2V-A14B), and a hybrid 5B variant that runs on consumer GPUs, all under Apache 2.0 for commercial use.</p>
<p>The workflow that makes sense: Mochi 1 or Wan 2.1 for prototyping locally. HunyuanVideo on cloud GPUs when you need final quality.</p>
<h2 id="image-generation" tabindex="-1">Image Generation <a class="header-anchor" href="#image-generation" aria-label="Permalink to &quot;Image Generation&quot;"></a></h2>
<p>This is where open-source already won. The models you can download today genuinely compete with Midjourney. Not &quot;almost as good&quot; — actually competitive.</p>
<p><img src="/img/ai-samples/flux-sample.png" alt="FLUX.1 generation samples">
<em>FLUX.1 samples — photorealistic quality from an Apache 2.0 licensed model</em></p>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Released</th>
<th>Params</th>
<th>Key Feature</th>
<th>License</th>
<th>Cost/image</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://huggingface.co/black-forest-labs/FLUX.1-schnell" target="_blank" rel="noreferrer"><strong>FLUX.1 [schnell]</strong></a></td>
<td>Black Forest Labs</td>
<td>Aug 2024</td>
<td>12B</td>
<td>4-step generation, fast</td>
<td>Apache 2.0</td>
<td>~$0.001</td>
</tr>
<tr>
<td><a href="https://huggingface.co/black-forest-labs/FLUX.1-dev" target="_blank" rel="noreferrer"><strong>FLUX.1 [dev]</strong></a></td>
<td>Black Forest Labs</td>
<td>Aug 2024</td>
<td>12B</td>
<td>Quality close to Pro</td>
<td>Non-commercial</td>
<td>~$0.002</td>
</tr>
<tr>
<td><a href="https://huggingface.co/stabilityai/stable-diffusion-3.5-large" target="_blank" rel="noreferrer"><strong>SD 3.5 Large</strong></a></td>
<td>Stability AI</td>
<td>Oct 2024</td>
<td>8B</td>
<td>Text rendering, diverse styles</td>
<td>Stability license</td>
<td>~$0.002</td>
</tr>
<tr>
<td><a href="https://huggingface.co/stabilityai/stable-diffusion-3.5-large-turbo" target="_blank" rel="noreferrer"><strong>SD 3.5 Large Turbo</strong></a></td>
<td>Stability AI</td>
<td>Oct 2024</td>
<td>8B</td>
<td>4-step, fast</td>
<td>Stability license</td>
<td>~$0.001</td>
</tr>
<tr>
<td><a href="https://github.com/zai-org/CogView4" target="_blank" rel="noreferrer"><strong>CogView4</strong></a></td>
<td>Tsinghua</td>
<td>Mar 2025</td>
<td>6B</td>
<td>Native Chinese text</td>
<td>Open</td>
<td>~$0.002</td>
</tr>
<tr>
<td><a href="https://bfl.ai/blog/flux-2" target="_blank" rel="noreferrer"><strong>FLUX.2</strong></a></td>
<td>Black Forest Labs</td>
<td>Nov 2025</td>
<td>32B</td>
<td>Text+edit, 4MP, multi-ref</td>
<td>dev: Non-commercial / klein: Apache 2.0</td>
<td>~$0.003</td>
</tr>
</tbody>
</table>
<p>Try them directly: <a href="https://huggingface.co/spaces/black-forest-labs/FLUX.1-schnell" target="_blank" rel="noreferrer">FLUX.1 schnell demo ↗</a> · <a href="https://huggingface.co/spaces/stabilityai/stable-diffusion-3.5-large" target="_blank" rel="noreferrer">SD 3.5 Large demo ↗</a> · <a href="https://github.com/black-forest-labs/flux" target="_blank" rel="noreferrer">GitHub (FLUX) ↗</a></p>
<p><strong>See samples:</strong> <a href="https://flux1.ai/examples" target="_blank" rel="noreferrer">FLUX gallery ↗</a> · <a href="https://flux1.ai/gallery" target="_blank" rel="noreferrer">FLUX LoRA gallery ↗</a> · <a href="https://replicate.com/black-forest-labs/flux-schnell/examples" target="_blank" rel="noreferrer">Replicate examples ↗</a></p>
<h3 id="for-building-game-assets" tabindex="-1">For building game assets <a class="header-anchor" href="#for-building-game-assets" aria-label="Permalink to &quot;For building game assets&quot;"></a></h3>
<p><strong>FLUX.1 [schnell]</strong> is the one to know. Apache 2.0 license — meaning you can ship commercial games without worrying about licensing drama. It generates in just 4 steps, so you can iterate fast. Describe what you want, see the result, adjust, repeat.</p>
<p><strong>SD 3.5 Large</strong> finally handles text rendering properly. Previous versions mangled any text you tried to include. This matters for UI mockups, in-game signage, title screens — anywhere you need readable words in your images.</p>
<p><strong>FLUX.2</strong> (Black Forest Labs, November 2025) is the bigger successor: a 32B model that handles text-to-image and image editing in one checkpoint, with strong multi-reference character/style consistency and reliable text rendering at up to 4 megapixels. The open-weight FLUX.2 [dev] uses a non-commercial license, but the size-distilled FLUX.2 [klein] ships under Apache 2.0, so there's still a commercial-safe option for shipping game art. Quantized pipelines bring [dev] down to 18-24GB GPUs.</p>
<p>The ecosystem around Stable Diffusion is still unmatched. ControlNet for precise composition. Inpainting for fixes. LoRA fine-tuning for custom styles. FLUX is catching up, but if you need deep customization today, SD's tooling maturity gives you more to work with.</p>
<p>Here's how I'd think about it: textures and sprites, either works. Concept art with specific style requirements, SD 3.5 with LoRAs. Pure quality for commercial shipping, FLUX schnell.</p>
<h2 id="_3d-generation" tabindex="-1">3D Generation <a class="header-anchor" href="#_3d-generation" aria-label="Permalink to &quot;3D Generation&quot;"></a></h2>
<p>If you've ever spent eight hours modeling a prop that appears in your game for three seconds, this section is for you. 3D generation went from &quot;interesting research&quot; to &quot;actually usable&quot; in 2024. You can now go from a sketch to a textured mesh in under a minute.</p>
<p><img src="/img/ai-samples/trellis-sample.png" alt="TRELLIS 3D generation samples">
<em>TRELLIS generates textured 3D meshes with PBR materials from single images</em></p>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Released</th>
<th>Key Feature</th>
<th>Output</th>
<th>Cost/mesh</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/microsoft/TRELLIS.2" target="_blank" rel="noreferrer"><strong>TRELLIS 2</strong></a></td>
<td>Microsoft</td>
<td>2025</td>
<td>4B params, PBR materials</td>
<td>Textured mesh with normals</td>
<td>~$0.03</td>
</tr>
<tr>
<td><a href="https://github.com/Tencent-Hunyuan/Hunyuan3D-2" target="_blank" rel="noreferrer"><strong>Hunyuan3D 2.0</strong></a></td>
<td>Tencent</td>
<td>Jan 2025</td>
<td>Two-stage DiT</td>
<td>High-fidelity textured mesh</td>
<td>~$0.05</td>
</tr>
<tr>
<td><a href="https://github.com/VAST-AI-Research/TripoSR" target="_blank" rel="noreferrer"><strong>TripoSR</strong></a></td>
<td>VAST/Stability</td>
<td>Mar 2024</td>
<td>Single image → mesh in 0.5s</td>
<td>Mesh (no texture)</td>
<td>~$0.001</td>
</tr>
<tr>
<td><a href="https://github.com/TencentARC/InstantMesh" target="_blank" rel="noreferrer"><strong>InstantMesh</strong></a></td>
<td>TencentARC</td>
<td>Apr 2024</td>
<td>Multi-view diffusion</td>
<td>Quality mesh</td>
<td>~$0.02</td>
</tr>
<tr>
<td><a href="https://huggingface.co/stabilityai/stable-zero123" target="_blank" rel="noreferrer"><strong>Stable Zero123</strong></a></td>
<td>Stability AI</td>
<td>2024</td>
<td>Novel view synthesis</td>
<td>Multi-view images</td>
<td>~$0.01</td>
</tr>
</tbody>
</table>
<p>Try them directly: <a href="https://huggingface.co/spaces/microsoft/TRELLIS.2" target="_blank" rel="noreferrer">TRELLIS 2 demo ↗</a> · <a href="https://huggingface.co/spaces/tencent/Hunyuan3D-2" target="_blank" rel="noreferrer">Hunyuan3D demo ↗</a> · <a href="https://huggingface.co/spaces/TencentARC/InstantMesh" target="_blank" rel="noreferrer">InstantMesh demo ↗</a></p>
<p><strong>See samples:</strong> <a href="https://microsoft.github.io/TRELLIS.2/" target="_blank" rel="noreferrer">TRELLIS 2 project page ↗</a> · <a href="https://www.3daistudio.com/Models/Trellis-2" target="_blank" rel="noreferrer">3D AI Studio gallery ↗</a></p>
<h3 id="a-workflow-that-actually-works" tabindex="-1">A workflow that actually works <a class="header-anchor" href="#a-workflow-that-actually-works" aria-label="Permalink to &quot;A workflow that actually works&quot;"></a></h3>
<p>The approach that's clicking for creators right now chains models together. Start with an image — generated or photographed, doesn't matter. Run it through Stable Zero123 or Wonder3D to get multiple views. Feed those views to InstantMesh or TripoSR for the mesh. Then TRELLIS 2 or Hunyuan3D for proper materials.</p>
<p><strong>TRELLIS 2</strong> from Microsoft is the new leader for production-ready assets. It handles the geometry that breaks other models — thin surfaces, holes, complex topology. The 4B parameter version outputs meshes with real PBR textures, not just vertex colors pretending to be materials.</p>
<p><strong>TripoSR</strong> is about speed. Half a second from image to mesh. The mesh needs cleanup and texturing, but for prototyping? For figuring out if an idea works before you invest hours? Unbeatable.</p>
<p><strong>Hunyuan3D 2.5</strong> focuses on simulation-ready assets. Game props that actually work in physics engines without manual fixes. No more invisible collision issues because the mesh topology is weird. Its LATTICE shape model scales up to 10B parameters for sharper detail, with PBR texturing built in.</p>
<p>Here's the realistic expectation for indie creators: generate concept art with FLUX, run it through InstantMesh for geometry, then texture in Blender or use TRELLIS for automated PBR. You're looking at 30-60 minutes per asset instead of 4-8 hours. Not zero time — but a real difference.</p>
<h2 id="audio-and-music" tabindex="-1">Audio and Music <a class="header-anchor" href="#audio-and-music" aria-label="Permalink to &quot;Audio and Music&quot;"></a></h2>
<p>Audio generation hasn't caught up to images and video yet. But there's enough here to change how you work — especially for prototyping and sound effects.</p>
<audio controls style="width: 100%;">
  <source src="/img/ai-samples/music-sample.mp3" type="audio/mpeg">
</audio>
<p><em>AI-generated music sample — describe the mood you want, get music that fits</em></p>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Released</th>
<th>What It Does</th>
<th>License</th>
<th>Cost/30s</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/ace-step/ACE-Step" target="_blank" rel="noreferrer"><strong>ACE-Step</strong></a></td>
<td>StepFun/ACE Studio</td>
<td>May 2025</td>
<td>~4 min music in ~20s, 19 languages, voice clone</td>
<td>Apache 2.0</td>
<td>~$0.02</td>
</tr>
<tr>
<td><a href="https://github.com/multimodal-art-projection/YuE" target="_blank" rel="noreferrer"><strong>YuE</strong></a></td>
<td>MAP</td>
<td>Jan 2025</td>
<td>Full songs from lyrics, vocals + accompaniment</td>
<td>Apache 2.0</td>
<td>~$0.05</td>
</tr>
<tr>
<td><a href="https://huggingface.co/facebook/musicgen-large" target="_blank" rel="noreferrer"><strong>MusicGen</strong></a></td>
<td>Meta</td>
<td>2023</td>
<td>Text-to-music, controllable</td>
<td>MIT</td>
<td>~$0.01</td>
</tr>
<tr>
<td><a href="https://huggingface.co/facebook/audiogen-medium" target="_blank" rel="noreferrer"><strong>AudioGen</strong></a></td>
<td>Meta</td>
<td>2023</td>
<td>Sound effects, ambient</td>
<td>MIT</td>
<td>~$0.01</td>
</tr>
<tr>
<td><a href="https://huggingface.co/stabilityai/stable-audio-open-1.0" target="_blank" rel="noreferrer"><strong>Stable Audio Open</strong></a></td>
<td>Stability AI</td>
<td>2024</td>
<td>Up to 47s samples</td>
<td>Research</td>
<td>~$0.02</td>
</tr>
</tbody>
</table>
<p>Try them directly: <a href="https://huggingface.co/spaces/facebook/MusicGen" target="_blank" rel="noreferrer">MusicGen demo ↗</a> · <a href="https://audiocraft.metademolab.com/musicgen.html" target="_blank" rel="noreferrer">AudioCraft playground ↗</a></p>
<p><strong>See samples:</strong> <a href="https://replicate.com/meta/musicgen/examples" target="_blank" rel="noreferrer">MusicGen examples ↗</a> · <a href="https://felixkreuk.github.io/audiogen/" target="_blank" rel="noreferrer">AudioGen samples ↗</a></p>
<h3 id="what-you-can-actually-ship-with" tabindex="-1">What you can actually ship with <a class="header-anchor" href="#what-you-can-actually-ship-with" aria-label="Permalink to &quot;What you can actually ship with&quot;"></a></h3>
<p><strong>MusicGen</strong> from Meta is the practical choice for game audio. Describe the mood you want, get music that fits. MIT license means you can ship it. The 3.3B model runs fine on a 12GB GPU — describe, generate, iterate.</p>
<p><strong>AudioGen</strong> handles sound effects: footsteps, door creaks, ambient wind, mechanical sounds. Same deal — MIT licensed, runs locally, actually useful for filling out your game's soundscape.</p>
<p><strong>YuE</strong> is genuinely exciting. It's the first open model that generates full songs with vocals. Theme songs. Background music with actual singing. The quality varies, but it's miles ahead of anything else you can download and run yourself.</p>
<p><strong>ACE-Step</strong> (StepFun and ACE Studio, May 2025) is the open music model worth knowing now. It's a 3.5B Apache 2.0 foundation model that generates roughly four minutes of music in about 20 seconds on an A100, supports 19 languages, and handles voice cloning, remixing, and lyric editing. For game prototyping it closes a lot of the gap that YuE and MusicGen left open.</p>
<p><strong>Stable Audio Open</strong> is limited. 47-second clips, research-only license. Good for prototyping ideas, not for shipping.</p>
<p>Here's the honest take: the gap between open models and closed ones (Suno, Udio) is still real for music. For sound effects, open models are genuinely competitive. For full songs you want to ship, expect to iterate heavily — or bring in a musician for final production and use these tools for everything else.</p>
<h2 id="speech-and-voice" tabindex="-1">Speech and Voice <a class="header-anchor" href="#speech-and-voice" aria-label="Permalink to &quot;Speech and Voice&quot;"></a></h2>
<p>Voice generation crossed into &quot;good enough for games&quot; territory in 2024. And that changes what's possible for small teams.</p>
<audio controls style="width: 100%;">
  <source src="/img/ai-samples/speech-sample.mp3" type="audio/mpeg">
</audio>
<p><em>AI-generated game narration — natural speech with proper pacing and emotion</em></p>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Released</th>
<th>Key Feature</th>
<th>License</th>
<th>Cost/min</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/SesameAILabs/csm" target="_blank" rel="noreferrer"><strong>CSM</strong></a></td>
<td>Sesame AI</td>
<td>Mar 2025</td>
<td>Conversational flow, natural pauses</td>
<td>Open</td>
<td>~$0.005</td>
</tr>
<tr>
<td><a href="https://github.com/fishaudio/fish-speech" target="_blank" rel="noreferrer"><strong>Fish Speech 1.5</strong></a></td>
<td>Fish Audio</td>
<td>2024</td>
<td>Zero-shot cloning from 10-30s</td>
<td>Apache 2.0</td>
<td>~$0.002</td>
</tr>
<tr>
<td><a href="https://github.com/myshell-ai/OpenVoice" target="_blank" rel="noreferrer"><strong>OpenVoice V2</strong></a></td>
<td>MyShell/MIT</td>
<td>Apr 2024</td>
<td>Emotion/accent control</td>
<td>MIT</td>
<td>~$0.003</td>
</tr>
<tr>
<td><a href="https://huggingface.co/coqui/XTTS-v2" target="_blank" rel="noreferrer"><strong>XTTS-v2</strong></a></td>
<td>Coqui (community)</td>
<td>2024</td>
<td>17 languages, voice cloning</td>
<td>CPML</td>
<td>~$0.005</td>
</tr>
</tbody>
</table>
<p><strong>Hear samples:</strong> <a href="https://fish.audio/" target="_blank" rel="noreferrer">Fish Audio voices ↗</a> · <a href="https://huggingface.co/spaces/myshell-ai/OpenVoice" target="_blank" rel="noreferrer">OpenVoice demo ↗</a></p>
<h3 id="making-npcs-sound-like-people" tabindex="-1">Making NPCs sound like people <a class="header-anchor" href="#making-npcs-sound-like-people" aria-label="Permalink to &quot;Making NPCs sound like people&quot;"></a></h3>
<p><strong>CSM (Conversational Speech Model)</strong> from Sesame was built specifically for dialogue. It produces natural pauses. Intonation shifts. The rhythm of actual conversation. Most TTS sounds like someone reading a script — you can hear it instantly. CSM sounds like someone talking. That difference matters more than you'd think.</p>
<p><strong>Fish Speech</strong> and <strong>OpenVoice</strong> handle voice cloning. Record 10-30 seconds of a voice actor, then generate unlimited dialogue in that voice. Think about what this means: you can hire voice talent for key lines, then extend their performance to cover hundreds of variations and ambient dialogue.</p>
<p><strong>NVIDIA ACE</strong> (not fully open, but worth knowing) now supports Qwen3-8B for on-device NPC deployment. Local LLM + local TTS + lip sync — all running on consumer GPUs. This is the stack for real-time NPC conversations that don't need cloud calls.</p>
<p>The approach that makes sense for indie creators: hire voice actors for main characters and the lines that matter most. Use Fish Speech or OpenVoice to extend coverage for ambient dialogue, variations, and all the incidental lines that would otherwise be silent or prohibitively expensive.</p>
<h2 id="world-models-and-game-simulation" tabindex="-1">World Models and Game Simulation <a class="header-anchor" href="#world-models-and-game-simulation" aria-label="Permalink to &quot;World Models and Game Simulation&quot;"></a></h2>
<p>This is where things get genuinely weird — and genuinely exciting. These models don't generate static assets. They generate experiences that feel like games.</p>
<a href="https://oasis.decart.ai/" target="_blank" style="display: block; background: linear-gradient(135deg, #2d5a27 0%, #1a3d16 100%); padding: 20px; border-radius: 12px; text-align: center; text-decoration: none; color: white;">
  <strong style="font-size: 18px;">🎮 Play Oasis — AI-Generated Minecraft</strong><br>
  <span style="opacity: 0.8;">Real-time world generation with no game engine, just AI prediction</span>
</a>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Released</th>
<th>What It Does</th>
<th>Status</th>
<th>Cost/frame</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/eloialonso/diamond" target="_blank" rel="noreferrer"><strong>DIAMOND</strong></a></td>
<td>Research</td>
<td>2024</td>
<td>Diffusion world model, Atari simulation</td>
<td>Open weights</td>
<td>~$0.001</td>
</tr>
<tr>
<td><a href="https://github.com/etched-ai/open-oasis" target="_blank" rel="noreferrer"><strong>Oasis</strong></a></td>
<td>Decart/Etched</td>
<td>Oct 2024</td>
<td>Real-time Minecraft generation</td>
<td>500M weights open</td>
<td>~$0.002</td>
</tr>
<tr>
<td><a href="https://github.com/GameGen-X/GameGen-X" target="_blank" rel="noreferrer"><strong>GameGen-X</strong></a></td>
<td>Research</td>
<td>2024</td>
<td>Open-world video generation</td>
<td>Open code + dataset</td>
<td>~$0.005</td>
</tr>
<tr>
<td><a href="https://huggingface.co/nvidia/Cosmos-1.0-Diffusion-7B-Text2World" target="_blank" rel="noreferrer"><strong>NVIDIA Cosmos</strong></a></td>
<td>NVIDIA</td>
<td>Jan 2025</td>
<td>Physical AI simulation</td>
<td>Open weights</td>
<td>~$0.01</td>
</tr>
<tr>
<td><a href="https://deepmind.google/blog/genie-2-a-large-scale-foundation-world-model/" target="_blank" rel="noreferrer"><strong>Genie 2</strong></a></td>
<td>DeepMind</td>
<td>Dec 2024</td>
<td>Interactive 3D from images</td>
<td>Not released</td>
<td>N/A</td>
</tr>
<tr>
<td><a href="https://deepmind.google/blog/genie-3-a-new-frontier-for-world-models/" target="_blank" rel="noreferrer"><strong>Genie 3</strong></a></td>
<td>DeepMind</td>
<td>Aug 2025</td>
<td>Real-time 720p worlds, promptable events</td>
<td>Closed (Project Genie)</td>
<td>N/A</td>
</tr>
</tbody>
</table>
<p>See the research: <a href="https://diamond-wm.github.io/" target="_blank" rel="noreferrer">DIAMOND project page ↗</a> · <a href="https://huggingface.co/blog/nvidia/cosmos-predict-and-transfer2-5" target="_blank" rel="noreferrer">Cosmos blog ↗</a></p>
<p><strong>Try it:</strong> <a href="https://oasis.decart.ai/starting-point" target="_blank" rel="noreferrer">Oasis live demo ↗</a> · <a href="https://deepmind.google/blog/genie-2-a-large-scale-foundation-world-model/" target="_blank" rel="noreferrer">Genie 2 examples ↗</a></p>
<h3 id="why-you-should-care-about-this" tabindex="-1">Why you should care about this <a class="header-anchor" href="#why-you-should-care-about-this" aria-label="Permalink to &quot;Why you should care about this&quot;"></a></h3>
<p><strong>DIAMOND</strong> proved something that changes how you think about game AI. You can train an agent entirely inside a generated world. No real game engine needed for training. The AI plays in a diffusion model's imagination — and then transfers to the real game. The implications here are significant.</p>
<p><strong>Oasis</strong> runs a Minecraft-like world in real-time. Frame by frame. No game engine, no textures, no pre-built assets. Just a transformer predicting what comes next. It's a proof of concept, but imagine where this goes. The 500M parameter version is already open.</p>
<p><strong>GameGen-X</strong> released the largest dataset for open-world game video. If you want to train your own models or fine-tune existing ones to generate game-like content, this is your starting point.</p>
<p><strong>NVIDIA Cosmos</strong> was built for robotics and autonomous vehicles, but the world foundation models work for games too. They understand physics. Object permanence. Spatial relationships. Open weights, permissive licensing.</p>
<p><strong>Genie 3</strong> (DeepMind, announced August 2025) is the leap worth noting: the first world model with real-time interaction, generating navigable 720p worlds at 24fps that stay consistent for a few minutes, plus 'promptable world events' that change weather or add objects on command. It opened to the public as Project Genie in January 2026 for Google AI Ultra subscribers in the US. Still closed weights, but it shows where playable, generated worlds are heading.</p>
<p>For practical game development today, these are still research tools. But if you're working on AI-driven content, procedural generation, or just thinking about where this is all going — this is the frontier.</p>
<h2 id="large-language-models" tabindex="-1">Large Language Models <a class="header-anchor" href="#large-language-models" aria-label="Permalink to &quot;Large Language Models&quot;"></a></h2>
<p>LLMs power dialogue, quest generation, and game logic. And the open options now genuinely compete with GPT-4. This wasn't true two years ago.</p>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Released</th>
<th>Size</th>
<th>Best For</th>
<th>License</th>
<th>Cost/1K tok</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/deepseek-ai/DeepSeek-V3" target="_blank" rel="noreferrer"><strong>DeepSeek-V3</strong></a></td>
<td>DeepSeek</td>
<td>Dec 2024</td>
<td>671B MoE (37B active)</td>
<td>Reasoning, general</td>
<td>Permissive</td>
<td>~$0.02</td>
</tr>
<tr>
<td><a href="https://huggingface.co/deepseek-ai/DeepSeek-V3" target="_blank" rel="noreferrer"><strong>DeepSeek-R1</strong></a></td>
<td>DeepSeek</td>
<td>Jan 2025</td>
<td>Based on V3</td>
<td>Chain-of-thought</td>
<td>Permissive</td>
<td>~$0.03</td>
</tr>
<tr>
<td><a href="https://huggingface.co/deepseek-ai/DeepSeek-V3.2" target="_blank" rel="noreferrer"><strong>DeepSeek-V3.2</strong></a></td>
<td>DeepSeek</td>
<td>Dec 2025</td>
<td>Sparse attention (DSA)</td>
<td>Reasoning + tool use</td>
<td>MIT</td>
<td>~$0.02</td>
</tr>
<tr>
<td><a href="https://github.com/QwenLM/Qwen3" target="_blank" rel="noreferrer"><strong>Qwen3</strong></a></td>
<td>Alibaba</td>
<td>2025</td>
<td>235B MoE (22B active)</td>
<td>Multilingual, code</td>
<td>Apache 2.0</td>
<td>~$0.01</td>
</tr>
<tr>
<td><a href="https://huggingface.co/collections/meta-llama/llama-4" target="_blank" rel="noreferrer"><strong>Llama 4</strong></a></td>
<td>Meta</td>
<td>2025</td>
<td>Various</td>
<td>Agents, 128k context</td>
<td>Llama Community</td>
<td>~$0.01</td>
</tr>
<tr>
<td><a href="https://github.com/deepseek-ai/DeepSeek-Coder-V2" target="_blank" rel="noreferrer"><strong>DeepSeek Coder V2</strong></a></td>
<td>DeepSeek</td>
<td>2024</td>
<td>—</td>
<td>300+ languages</td>
<td>Permissive</td>
<td>~$0.01</td>
</tr>
<tr>
<td><a href="https://github.com/QwenLM/Qwen3-VL" target="_blank" rel="noreferrer"><strong>Qwen2.5-VL</strong></a></td>
<td>Alibaba</td>
<td>Jan 2025</td>
<td>7B-72B</td>
<td>Vision + language</td>
<td>Permissive</td>
<td>~$0.02</td>
</tr>
</tbody>
</table>
<p>Get started: <a href="https://huggingface.co/Qwen/Qwen3-8B" target="_blank" rel="noreferrer">Qwen3-8B on HuggingFace ↗</a> · <a href="https://huggingface.co/deepseek-ai/DeepSeek-V3" target="_blank" rel="noreferrer">DeepSeek-V3 on HuggingFace ↗</a></p>
<h3 id="for-building-games" tabindex="-1">For building games <a class="header-anchor" href="#for-building-games" aria-label="Permalink to &quot;For building games&quot;"></a></h3>
<p><strong>Qwen3</strong> is the practical choice for most game uses. Apache 2.0 license — meaning you own your integration. Strong multilingual support, which matters if you're thinking about localization. Good at following structured instructions. The 7B and 14B variants run locally on consumer GPUs.</p>
<p><strong>DeepSeek-V3</strong> matches or beats GPT-4 on most benchmarks. The architecture is clever — only 37B parameters activate per token despite the 671B total. You need serious hardware (multi-GPU), but the quality is frontier-level without the API dependency.</p>
<p><strong>DeepSeek-V3.2</strong> (December 2025) is the current open DeepSeek frontier. It folds reasoning and tool-use into one model and introduces DeepSeek Sparse Attention (DSA) for cheaper long-context inference, with a high-compute Speciale variant aimed at the top reasoning benchmarks. For game logic and dialogue it's a stronger, more agent-capable drop-in than V3.</p>
<p><strong>Qwen2.5-VL</strong> adds vision understanding. Useful for games that need to analyze screenshots, understand player-drawn content, or process camera input. The 7B variant runs on a single GPU.</p>
<p>For on-device NPCs — characters that respond in real-time without cloud calls — <strong>Qwen3-8B</strong> through NVIDIA ACE is the most practical path right now. It runs alongside your game on the player's hardware.</p>
<h2 id="utility-models" tabindex="-1">Utility Models <a class="header-anchor" href="#utility-models" aria-label="Permalink to &quot;Utility Models&quot;"></a></h2>
<p>These don't generate content directly — but they make your pipelines work.</p>
<p><img src="/img/ai-samples/sam2-sample.jpg" alt="SAM 2 segmentation">
<em>SAM 2 segments any object in images and video — click once, get a perfect mask</em></p>
<table tabindex="0">
<thead>
<tr>
<th>Model</th>
<th>Org</th>
<th>Released</th>
<th>What It Does</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/facebookresearch/sam2" target="_blank" rel="noreferrer"><strong>SAM 2</strong></a></td>
<td>Meta</td>
<td>Aug 2024</td>
<td>Segment anything in images and video</td>
</tr>
<tr>
<td><a href="https://github.com/apple/ml-depth-pro" target="_blank" rel="noreferrer"><strong>Depth Pro</strong></a></td>
<td>Apple</td>
<td>Oct 2024</td>
<td>Metric depth from single image</td>
</tr>
<tr>
<td><a href="https://github.com/nerfstudio-project/gsplat" target="_blank" rel="noreferrer"><strong>gsplat</strong></a></td>
<td>Nerfstudio</td>
<td>2024+</td>
<td>Gaussian splatting, CUDA accelerated</td>
</tr>
</tbody>
</table>
<p><strong>SAM 2</strong> segments objects in video in real-time. Click on something, get a perfect mask. Useful for rotoscoping, compositing, or extracting objects from footage to use as game assets. <a href="https://github.com/huggingface/sam2-studio" target="_blank" rel="noreferrer">Try SAM 2 ↗</a></p>
<p><strong>Depth Pro</strong> from Apple produces metric depth maps from single images in under a second. This unlocks a lot: converting 2D art to 2.5D with parallax effects, generating depth data for 3D reconstruction, creating normal maps from flat images. <a href="https://huggingface.co/apple/DepthPro-hf" target="_blank" rel="noreferrer">Depth Pro on HuggingFace ↗</a></p>
<p><strong>gsplat</strong> is the fast implementation of Gaussian splatting. If you're capturing real environments for games — photogrammetry, environment scans — this is the library that makes it practical.</p>
<h2 id="what-i-d-actually-use" tabindex="-1">What I'd Actually Use <a class="header-anchor" href="#what-i-d-actually-use" aria-label="Permalink to &quot;What I'd Actually Use&quot;"></a></h2>
<p>If you're starting a game project today, here's the stack that makes sense:</p>
<p><strong>Textures and sprites</strong>: FLUX.1 [schnell] — Apache 2.0, fast iteration, quality that ships</p>
<p><strong>Concept art</strong>: SD 3.5 Large with LoRAs for style control</p>
<p><strong>3D assets</strong>: InstantMesh for geometry, then Blender for texturing or TRELLIS 2 for automated PBR</p>
<p><strong>Sound effects</strong>: AudioGen — MIT licensed, runs locally, fills out your soundscape</p>
<p><strong>Music</strong>: MusicGen for prototypes, then bring in a composer for final production</p>
<p><strong>Voice</strong>: Fish Speech for prototyping, voice actors + cloning for production</p>
<p><strong>NPC dialogue</strong>: Qwen3-8B locally, or cloud LLM for complex reasoning</p>
<p><strong>Video (cutscenes)</strong>: Mochi 1 locally, HunyuanVideo on cloud when you need final quality</p>
<p>Here's the thing about all of this: the common mistake is trying to use AI for everything. These are tools, not replacements. They compress the tedious parts — iteration, variations, placeholder assets — so you can spend your time on the creative decisions that actually matter. The parts that make your game yours.</p>
<h2 id="hardware-reality-check" tabindex="-1">Hardware Reality Check <a class="header-anchor" href="#hardware-reality-check" aria-label="Permalink to &quot;Hardware Reality Check&quot;"></a></h2>
<p>Let's be honest about what you actually need to run this stuff:</p>
<p><strong>8GB VRAM</strong> (RTX 3060, 4060): SD 1.5/SDXL, Wan 2.1 small, AudioGen, Fish Speech, small LLMs (7B quantized). This is gaming laptop territory — and it's enough to get started.</p>
<p><strong>12GB VRAM</strong> (RTX 3080, 4070): SD 3.5, FLUX schnell, Mochi 1, MusicGen, TripoSR, Qwen 14B quantized. This is where things get comfortable. Most of the useful models run here.</p>
<p><strong>24GB VRAM</strong> (RTX 3090, 4090): Most models at full precision, InstantMesh, larger LLMs. If you're serious about this workflow, this is the sweet spot.</p>
<p><strong>48-80GB VRAM</strong> (A100, H100): HunyuanVideo, LTX-2, DeepSeek-V3, production-scale generation. Enterprise hardware. You're not buying this — you're renting it.</p>
<p>Cloud instances on RunPod, Lambda Labs, or Modal cost $2-4/hour for A100s. For occasional use, that's cheaper than hardware. Spin up when you need final quality, shut down when you're done.</p>
<p><strong>About the cost estimates in this guide</strong>: Per-generation costs assume self-hosted inference on cloud GPUs at ~$2-3/hour (A100) or ~$0.40/hour (RTX 4090). Actual costs vary based on hardware, optimization, and batch sizes. These are ballpark figures for planning — your mileage will vary.</p>
<h2 id="what-s-new-in-2026" tabindex="-1">What's New in 2026 <a class="header-anchor" href="#what-s-new-in-2026" aria-label="Permalink to &quot;What's New in 2026&quot;"></a></h2>
<p><strong>Just released</strong>: LTX-2 weights dropped — the first open model with synchronized audio and video. Hunyuan3D 2.5 is now available for simulation-ready 3D assets that work in physics engines.</p>
<p><strong>Coming this year</strong>: Real-time video generation with sub-second latency. Better world models for game simulation. And smaller models that run on integrated graphics — meaning laptops without dedicated GPUs.</p>
<p>The trajectory is clear: every capability that exists in closed models shows up in open models 6-12 months later. The question isn't whether open models will be good enough — they already are for most uses. The question is how fast they become the default.</p>
<p>And here's what that means for creators: the tools that used to require enterprise budgets or monthly subscriptions are becoming something you can just... run. On your own hardware. With no one else's permission.</p>
<p>That's the shift. That's what we're building toward.</p>
<hr>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="what-is-the-best-open-source-ai-model-for-generating-game-assets" tabindex="-1">What is the best open-source AI model for generating game assets? <a class="header-anchor" href="#what-is-the-best-open-source-ai-model-for-generating-game-assets" aria-label="Permalink to &quot;What is the best open-source AI model for generating game assets?&quot;"></a></h3>
<p>It depends on the asset. For 3D models, Hunyuan3D is the strongest open option in 2026. For 2D art and textures, FLUX leads on quality. For sound effects and music, the open audio models have caught up fast. There's no single &quot;best&quot; model because games need many asset types, so most creators chain a few together rather than relying on one.</p>
<h3 id="are-open-source-ai-models-good-enough-to-replace-closed-apis" tabindex="-1">Are open-source AI models good enough to replace closed APIs? <a class="header-anchor" href="#are-open-source-ai-models-good-enough-to-replace-closed-apis" aria-label="Permalink to &quot;Are open-source AI models good enough to replace closed APIs?&quot;"></a></h3>
<p>For most game-asset work in 2026, yes. Open models now match closed APIs on image, 3D, and audio generation, and you can run them on your own hardware with no per-call fees or surprise pricing changes. Closed models still lead on a few frontier tasks like long-form video, but the gap usually closes within 6 to 12 months.</p>
<h3 id="can-i-run-these-generative-ai-models-on-my-own-gpu" tabindex="-1">Can I run these generative AI models on my own GPU? <a class="header-anchor" href="#can-i-run-these-generative-ai-models-on-my-own-gpu" aria-label="Permalink to &quot;Can I run these generative AI models on my own GPU?&quot;"></a></h3>
<p>Many of them, yes. Image and audio models run comfortably on a single consumer GPU like an RTX 4090. Larger video and 3D models want more VRAM and often an A100-class cloud GPU. The hardware section above lists what each model needs so you can plan before you commit.</p>
<h3 id="is-it-legal-to-use-ai-generated-assets-in-a-commercial-game" tabindex="-1">Is it legal to use AI-generated assets in a commercial game? <a class="header-anchor" href="#is-it-legal-to-use-ai-generated-assets-in-a-commercial-game" aria-label="Permalink to &quot;Is it legal to use AI-generated assets in a commercial game?&quot;"></a></h3>
<p>Generally yes for open-source models you run yourself, but it depends on the model's license and your training-data assumptions. Always check the specific model license, and disclose AI-generated content where your platform requires it. See our <a href="/ai-content.html">AI-generated content policy</a> for how we handle this.</p>
<hr>
<h2 id="more-reading" tabindex="-1">More Reading <a class="header-anchor" href="#more-reading" aria-label="Permalink to &quot;More Reading&quot;"></a></h2>
<ul>
<li><a href="/blog/2026-01-18-ai-controversy-and-post-ai-economy.html">AI controversy, trust, and the post-AI economy</a> — our position on AI in games</li>
<li><a href="/ai-content.html">AI-generated content policy</a> — how we handle AI disclosure</li>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> — WebGL, WebGPU, and Wasm for games</li>
<li><a href="/guides/browser-3d-open-world-tech.html">Browser 3D Open World Tech</a> — using AI-generated 3D assets in browser worlds</li>
<li><a href="/guides/landscape-generation-browser.html">Landscape Generation for Browser Open Worlds</a> — diffusion-based terrain synthesis</li>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> — traditional asset sources alongside AI generation</li>
<li><a href="/tutorials/agentic-code-tools.html">Agentic AI code tools</a> — AI for writing game code, not just generating assets</li>
</ul>
]]></content:encoded>
            <enclosure url="https://app.cinevva.com/img/ai-samples/flux-sample.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Game Asset Licenses Explained (CC0, CC-BY, Royalty-Free)]]></title>
            <link>https://app.cinevva.com/guides/game-asset-licenses</link>
            <guid>https://app.cinevva.com/guides/game-asset-licenses</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How game asset licenses work in plain English - CC0, CC-BY, CC-BY-SA, royalty-free, editorial, MIT, and GPL - and which are safe to use in a commercial game.]]></description>
            <content:encoded><![CDATA[<h1 id="game-asset-licenses-explained-cc0-cc-by-royalty-free" tabindex="-1">Game Asset Licenses Explained (CC0, CC-BY, Royalty-Free) <a class="header-anchor" href="#game-asset-licenses-explained-cc0-cc-by-royalty-free" aria-label="Permalink to &quot;Game Asset Licenses Explained (CC0, CC-BY, Royalty-Free)&quot;"></a></h1>
<p><em>Last updated: June 2026.</em></p>
<p>Everything you need to know about asset licenses so you can confidently use free and paid assets in your games without legal trouble.</p>
<h2 id="why-licenses-matter" tabindex="-1">Why Licenses Matter <a class="header-anchor" href="#why-licenses-matter" aria-label="Permalink to &quot;Why Licenses Matter&quot;"></a></h2>
<p>Every creative work - 3D models, textures, sounds, music - is protected by copyright. The creator owns it by default. A <strong>license</strong> is the creator's permission for you to use their work under specific terms.</p>
<p><strong>Using assets without understanding their license can lead to:</strong></p>
<ul>
<li>DMCA takedowns of your game</li>
<li>Legal action from copyright holders</li>
<li>Forced removal from app stores</li>
<li>Financial damages</li>
</ul>
<p><strong>The good news:</strong> Most game assets have clear, permissive licenses. You just need to understand what they allow.</p>
<hr>
<h2 id="license-quick-reference" tabindex="-1">License Quick Reference <a class="header-anchor" href="#license-quick-reference" aria-label="Permalink to &quot;License Quick Reference&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>License</th>
<th>Commercial Use</th>
<th>Attribution</th>
<th>Modify</th>
<th>Share Alike</th>
<th>Providers</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CC0</strong></td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>Poly Haven, Kenney, Quaternius, AmbientCG</td>
</tr>
<tr>
<td><strong>CC-BY</strong></td>
<td>✅ Yes</td>
<td>✅ Required</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>Sketchfab, OpenGameArt, Freesound</td>
</tr>
<tr>
<td><strong>CC-BY-SA</strong></td>
<td>✅ Yes</td>
<td>✅ Required</td>
<td>✅ Yes</td>
<td>✅ Required</td>
<td>OpenGameArt, some Freesound</td>
</tr>
<tr>
<td><strong>CC-BY-NC</strong></td>
<td>❌ No</td>
<td>✅ Required</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>Some Sketchfab, itch.io</td>
</tr>
<tr>
<td><strong>CC-BY-NC-SA</strong></td>
<td>❌ No</td>
<td>✅ Required</td>
<td>✅ Yes</td>
<td>✅ Required</td>
<td>Various</td>
</tr>
<tr>
<td><strong>Royalty-Free</strong></td>
<td>✅ Yes</td>
<td>❌ Usually no</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>Synty, CGTrader, TurboSquid</td>
</tr>
<tr>
<td><strong>Editorial Only</strong></td>
<td>❌ No*</td>
<td>Varies</td>
<td>⚠️ Limited</td>
<td>❌ No</td>
<td>TurboSquid (some)</td>
</tr>
<tr>
<td><strong>MIT</strong></td>
<td>✅ Yes</td>
<td>✅ Required</td>
<td>✅ Yes</td>
<td>❌ No</td>
<td>Three.js, pmndrs</td>
</tr>
<tr>
<td><strong>GPL</strong></td>
<td>✅ Yes</td>
<td>✅ Required</td>
<td>✅ Yes</td>
<td>✅ Required</td>
<td>Some OpenGameArt</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="creative-commons-licenses" tabindex="-1">Creative Commons Licenses <a class="header-anchor" href="#creative-commons-licenses" aria-label="Permalink to &quot;Creative Commons Licenses&quot;"></a></h2>
<p>Creative Commons (CC) licenses are the most common for free game assets. They're standardized, internationally recognized, and easy to understand.</p>
<h3 id="cc0-public-domain-dedication" tabindex="-1">CC0 - Public Domain Dedication <a class="header-anchor" href="#cc0-public-domain-dedication" aria-label="Permalink to &quot;CC0 - Public Domain Dedication&quot;"></a></h3>
<p><strong>The most permissive license. Treat it as if no copyright exists.</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially</td>
<td>✅</td>
</tr>
<tr>
<td>Use in closed-source games</td>
<td>✅</td>
</tr>
<tr>
<td>Modify, remix, transform</td>
<td>✅</td>
</tr>
<tr>
<td>Distribute and share</td>
<td>✅</td>
</tr>
<tr>
<td>Use without credit</td>
<td>✅</td>
</tr>
<tr>
<td>Claim as your own work</td>
<td>⚠️ Legally yes, ethically questionable</td>
</tr>
</tbody>
</table>
<p><strong>In plain English:</strong> Do whatever you want. No restrictions. No attribution required.</p>
<p><strong>Providers using CC0:</strong></p>
<ul>
<li>Poly Haven (all assets)</li>
<li>Kenney (all assets)</li>
<li>Quaternius (all assets)</li>
<li>AmbientCG (all assets)</li>
<li>Some OpenGameArt, Freesound, Sketchfab</li>
</ul>
<p><strong>Example use:</strong> Download a spaceship from Kenney, modify it, sell your game for $20, never mention Kenney anywhere. Perfectly legal.</p>
<p><strong>Why creators choose CC0:</strong> To maximize adoption. Many creators want their work used widely and don't care about attribution.</p>
<hr>
<h3 id="cc-by-attribution" tabindex="-1">CC-BY - Attribution <a class="header-anchor" href="#cc-by-attribution" aria-label="Permalink to &quot;CC-BY - Attribution&quot;"></a></h3>
<p><strong>Use freely, but you must give credit.</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially</td>
<td>✅</td>
</tr>
<tr>
<td>Use in closed-source games</td>
<td>✅</td>
</tr>
<tr>
<td>Modify, remix, transform</td>
<td>✅</td>
</tr>
<tr>
<td>Distribute and share</td>
<td>✅</td>
</tr>
<tr>
<td>Use without credit</td>
<td>❌ Must attribute</td>
</tr>
</tbody>
</table>
<p><strong>Attribution requirements:</strong></p>
<ol>
<li>Name the creator</li>
<li>Provide the license name (CC-BY 4.0)</li>
<li>Link to the license (if reasonable)</li>
<li>Indicate if changes were made</li>
</ol>
<p><strong>In plain English:</strong> Use it however you want, but give credit to the creator.</p>
<p><strong>Where to put attribution:</strong></p>
<ul>
<li>Credits screen in your game</li>
<li>README or CREDITS.txt file</li>
<li>About/Legal section</li>
<li>Game's website</li>
</ul>
<p><strong>Example attribution:</strong></p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>"Spaceship Model" by John Artist</span></span>
<span class="line"><span>Licensed under CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)</span></span>
<span class="line"><span>Modified: Adjusted scale and added emission texture</span></span></code></pre>
</div><p><strong>Providers using CC-BY:</strong></p>
<ul>
<li>Many Sketchfab models</li>
<li>Many OpenGameArt assets</li>
<li>Some Freesound audio</li>
<li>BlendSwap</li>
</ul>
<p><strong>A note on Sketchfab:</strong> Its paid store has closed and selling moved to Epic's Fab marketplace, but free Creative Commons and CC0 downloads still work on Sketchfab today. Sketchfab has said it will give advance notice before free downloadable content goes away, so if you're sourcing CC-BY or CC0 models there, grab and archive what you need rather than assuming a link will live forever.</p>
<hr>
<h3 id="cc-by-sa-attribution-sharealike" tabindex="-1">CC-BY-SA - Attribution ShareAlike <a class="header-anchor" href="#cc-by-sa-attribution-sharealike" aria-label="Permalink to &quot;CC-BY-SA - Attribution ShareAlike&quot;"></a></h3>
<p><strong>Use freely with credit, but derivatives must use the same license.</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially</td>
<td>✅</td>
</tr>
<tr>
<td>Use in closed-source games</td>
<td>✅ (the game itself)</td>
</tr>
<tr>
<td>Modify, remix, transform</td>
<td>✅</td>
</tr>
<tr>
<td>Distribute and share</td>
<td>✅</td>
</tr>
<tr>
<td>Use without credit</td>
<td>❌ Must attribute</td>
</tr>
<tr>
<td>Keep derivatives proprietary</td>
<td>❌ Must use same license</td>
</tr>
</tbody>
</table>
<p><strong>The &quot;ShareAlike&quot; catch:</strong> If you modify the asset and distribute the modified version, it must also be CC-BY-SA.</p>
<p><strong>Important clarification for games:</strong></p>
<ul>
<li>Your <strong>game code</strong> does NOT need to be open source</li>
<li>Your <strong>game</strong> can be commercial and closed-source</li>
<li>Only the <strong>asset itself</strong> (and derivatives of it) must remain CC-BY-SA</li>
<li>If someone extracts the asset from your game, they can use it under CC-BY-SA</li>
</ul>
<p><strong>In plain English:</strong> Credit the creator. If you share a modified version of the asset, it stays CC-BY-SA.</p>
<p><strong>When to be careful:</strong></p>
<ul>
<li>If you're creating an asset pack or template</li>
<li>If you're redistributing assets in a toolkit</li>
<li>If your game allows users to extract raw assets</li>
</ul>
<hr>
<h3 id="cc-by-nc-attribution-noncommercial" tabindex="-1">CC-BY-NC - Attribution NonCommercial <a class="header-anchor" href="#cc-by-nc-attribution-noncommercial" aria-label="Permalink to &quot;CC-BY-NC - Attribution NonCommercial&quot;"></a></h3>
<p><strong>Free to use with credit, but NOT for commercial purposes.</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially</td>
<td>❌ No</td>
</tr>
<tr>
<td>Use in closed-source games</td>
<td>✅ (if game is free)</td>
</tr>
<tr>
<td>Modify, remix, transform</td>
<td>✅</td>
</tr>
<tr>
<td>Distribute and share</td>
<td>✅ (non-commercially)</td>
</tr>
<tr>
<td>Use without credit</td>
<td>❌ Must attribute</td>
</tr>
</tbody>
</table>
<p><strong>What counts as &quot;commercial&quot;?</strong></p>
<ul>
<li>❌ Selling the game</li>
<li>❌ In-app purchases</li>
<li>❌ Ads in the game</li>
<li>❌ Patreon-funded game development</li>
<li>⚠️ Free game by a company (gray area)</li>
<li>✅ Purely free hobby project with no monetization</li>
</ul>
<p><strong>In plain English:</strong> Only use in completely free, non-monetized projects.</p>
<p><strong>Recommendation:</strong> Avoid NC-licensed assets for any project that might generate revenue. The definition of &quot;commercial&quot; is broad and contested.</p>
<hr>
<h3 id="cc-by-nc-sa-attribution-noncommercial-sharealike" tabindex="-1">CC-BY-NC-SA - Attribution NonCommercial ShareAlike <a class="header-anchor" href="#cc-by-nc-sa-attribution-noncommercial-sharealike" aria-label="Permalink to &quot;CC-BY-NC-SA - Attribution NonCommercial ShareAlike&quot;"></a></h3>
<p><strong>The most restrictive Creative Commons license.</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially</td>
<td>❌ No</td>
</tr>
<tr>
<td>Use in closed-source games</td>
<td>✅ (if game is free)</td>
</tr>
<tr>
<td>Modify, remix, transform</td>
<td>✅</td>
</tr>
<tr>
<td>Distribute and share</td>
<td>✅ (non-commercially)</td>
</tr>
<tr>
<td>Use without credit</td>
<td>❌ Must attribute</td>
</tr>
<tr>
<td>Keep derivatives proprietary</td>
<td>❌ Must use same license</td>
</tr>
</tbody>
</table>
<p><strong>In plain English:</strong> Non-commercial only, must credit, and any derivatives must also be NC-SA.</p>
<p><strong>Recommendation:</strong> Generally avoid for game development unless you're certain your project will never be monetized in any way.</p>
<hr>
<h2 id="royalty-free-licenses" tabindex="-1">Royalty-Free Licenses <a class="header-anchor" href="#royalty-free-licenses" aria-label="Permalink to &quot;Royalty-Free Licenses&quot;"></a></h2>
<p>&quot;Royalty-Free&quot; is a commercial licensing model, NOT a Creative Commons variant.</p>
<h3 id="what-royalty-free-actually-means" tabindex="-1">What &quot;Royalty-Free&quot; Actually Means <a class="header-anchor" href="#what-royalty-free-actually-means" aria-label="Permalink to &quot;What &quot;Royalty-Free&quot; Actually Means&quot;"></a></h3>
<p><strong>Royalty-Free ≠ Free</strong></p>
<p>&quot;Royalty-Free&quot; means:</p>
<ul>
<li>You pay <strong>once</strong> (or get it free during a promotion)</li>
<li>You can use it <strong>unlimited times</strong></li>
<li>You don't pay <strong>per-use royalties</strong></li>
</ul>
<p><strong>Compare to &quot;Rights-Managed&quot; (the alternative):</strong></p>
<ul>
<li>Pay per specific use</li>
<li>Limited to certain media, territories, time periods</li>
<li>More expensive, more restrictive</li>
</ul>
<h3 id="standard-royalty-free-terms" tabindex="-1">Standard Royalty-Free Terms <a class="header-anchor" href="#standard-royalty-free-terms" aria-label="Permalink to &quot;Standard Royalty-Free Terms&quot;"></a></h3>
<p>Most marketplaces (Synty, CGTrader, TurboSquid) use similar terms:</p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially in games</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Use in multiple projects</td>
<td>✅ Yes (often unlimited)</td>
</tr>
<tr>
<td>Modify and adapt</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Credit the creator</td>
<td>❌ Usually not required</td>
</tr>
<tr>
<td>Resell the raw asset file</td>
<td>❌ No</td>
</tr>
<tr>
<td>Let users extract the asset</td>
<td>❌ No</td>
</tr>
<tr>
<td>Transfer license to others</td>
<td>❌ No</td>
</tr>
</tbody>
</table>
<p><strong>Key restrictions:</strong></p>
<ol>
<li><strong>No redistribution</strong> - Can't give away or sell the asset files</li>
<li><strong>No extraction</strong> - Must be &quot;incorporated&quot; so users can't easily extract it</li>
<li><strong>One licensee</strong> - The license is for you/your team, not transferable</li>
</ol>
<p><strong>In plain English:</strong> Use it in your games, but don't let others get the raw files.</p>
<h3 id="synty-studios-license" tabindex="-1">Synty Studios License <a class="header-anchor" href="#synty-studios-license" aria-label="Permalink to &quot;Synty Studios License&quot;"></a></h3>
<p>Synty now offers two license models: a <strong>one-time purchase license</strong> (perpetual rights to the specific packs you buy) and a <strong>Standard Subscription license</strong> (access to the whole library while your subscription is active). Both are royalty-free for commercial use once paid.</p>
<p>One-time purchase terms:</p>
<ul>
<li>✅ Use in unlimited commercial projects</li>
<li>✅ Modify assets freely</li>
<li>✅ 5 seats (team members) per purchase (Humble Bundle copies include 1 seat)</li>
<li>❌ Cannot resell, redistribute, or share assets outside your seated team</li>
<li>❌ Cannot use in asset flipping (minimal changes then resell)</li>
<li>❌ Cannot use Synty's name or logo, or claim you created the assets</li>
<li>❌ Cannot include assets in generative-AI training datasets, NFTs/blockchain products, or metaverse products (these need a custom license)</li>
</ul>
<h3 id="cgtrader-royalty-free-license" tabindex="-1">CGTrader Royalty-Free License <a class="header-anchor" href="#cgtrader-royalty-free-license" aria-label="Permalink to &quot;CGTrader Royalty-Free License&quot;"></a></h3>
<ul>
<li>✅ Commercial use in games, films, VR/AR</li>
<li>✅ Modify and adapt</li>
<li>❌ Cannot sell or give away the model file</li>
<li>❌ Cannot allow users to extract the model</li>
<li>⚠️ Some models marked &quot;No AI&quot; - can't use for ML training</li>
</ul>
<h3 id="turbosquid-3d-model-license" tabindex="-1">TurboSquid 3D Model License <a class="header-anchor" href="#turbosquid-3d-model-license" aria-label="Permalink to &quot;TurboSquid 3D Model License&quot;"></a></h3>
<ul>
<li>✅ Commercial use (unless marked &quot;Editorial&quot;)</li>
<li>✅ Modify and adapt</li>
<li>✅ Up to 5 physical prints for personal/charitable use</li>
<li>❌ Cannot redistribute model files</li>
<li>❌ Cannot use in logo/trademark</li>
<li>⚠️ Check for &quot;Editorial Only&quot; label</li>
<li>⚠️ Check for Depicted IP (brand logos)</li>
</ul>
<hr>
<h2 id="editorial-use-only" tabindex="-1">Editorial Use Only <a class="header-anchor" href="#editorial-use-only" aria-label="Permalink to &quot;Editorial Use Only&quot;"></a></h2>
<p>Some assets are marked &quot;Editorial Use Only&quot; - this is important to understand.</p>
<h3 id="what-it-means" tabindex="-1">What It Means <a class="header-anchor" href="#what-it-means" aria-label="Permalink to &quot;What It Means&quot;"></a></h3>
<p>&quot;Editorial&quot; use includes:</p>
<ul>
<li>News reporting</li>
<li>Educational content</li>
<li>Documentary</li>
<li>Commentary and criticism</li>
</ul>
<p><strong>NOT editorial:</strong></p>
<ul>
<li>Commercial games</li>
<li>Advertising</li>
<li>Marketing materials</li>
<li>Products for sale</li>
</ul>
<h3 id="why-assets-get-this-label" tabindex="-1">Why Assets Get This Label <a class="header-anchor" href="#why-assets-get-this-label" aria-label="Permalink to &quot;Why Assets Get This Label&quot;"></a></h3>
<p>Usually because they contain:</p>
<ul>
<li><strong>Trademarks</strong> - Brand logos (Nike, Apple, etc.)</li>
<li><strong>Likenesses</strong> - Real people's faces</li>
<li><strong>Copyrighted designs</strong> - Car designs, product designs</li>
<li><strong>Intellectual property</strong> - Characters, recognizable items</li>
</ul>
<h3 id="example" tabindex="-1">Example <a class="header-anchor" href="#example" aria-label="Permalink to &quot;Example&quot;"></a></h3>
<p>A 3D model of a Ferrari might be &quot;Editorial Only&quot; because:</p>
<ul>
<li>Ferrari's design is protected</li>
<li>Using it in a game could imply endorsement</li>
<li>Commercial use requires Ferrari's permission</li>
</ul>
<p><strong>Recommendation:</strong> Avoid &quot;Editorial Only&quot; assets entirely for game development. Use original or properly licensed alternatives.</p>
<hr>
<h2 id="software-licenses-mit-gpl-apache" tabindex="-1">Software Licenses (MIT, GPL, Apache) <a class="header-anchor" href="#software-licenses-mit-gpl-apache" aria-label="Permalink to &quot;Software Licenses (MIT, GPL, Apache)&quot;"></a></h2>
<p>These primarily apply to code but sometimes cover assets too.</p>
<h3 id="mit-license" tabindex="-1">MIT License <a class="header-anchor" href="#mit-license" aria-label="Permalink to &quot;MIT License&quot;"></a></h3>
<p><strong>Very permissive. Common for code and code-adjacent assets.</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Modify</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Distribute</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Keep proprietary</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Attribution</td>
<td>✅ Required (in license file)</td>
</tr>
</tbody>
</table>
<p><strong>Attribution requirement:</strong> Include the original license text somewhere in your project (usually in a LICENSES file or about screen).</p>
<p><strong>Example MIT notice:</strong></p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>The MIT License (MIT)</span></span>
<span class="line"><span>Copyright (c) 2024 Original Author</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Permission is hereby granted, free of charge, to any person obtaining a copy...</span></span></code></pre>
</div><p><strong>Assets using MIT:</strong></p>
<ul>
<li>Three.js examples</li>
<li>pmndrs/vanilla effects</li>
<li>Many npm packages with assets</li>
</ul>
<h3 id="gpl-gnu-general-public-license" tabindex="-1">GPL (GNU General Public License) <a class="header-anchor" href="#gpl-gnu-general-public-license" aria-label="Permalink to &quot;GPL (GNU General Public License)&quot;"></a></h3>
<p><strong>Copyleft license. Derivatives must also be GPL.</strong></p>
<table tabindex="0">
<thead>
<tr>
<th>What You Can Do</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>Use commercially</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Modify</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Distribute</td>
<td>✅ Yes</td>
</tr>
<tr>
<td>Keep proprietary</td>
<td>❌ No (if distributing)</td>
</tr>
<tr>
<td>Attribution</td>
<td>✅ Required</td>
</tr>
</tbody>
</table>
<p><strong>The catch:</strong> If you distribute GPL-licensed work (or derivatives), you must:</p>
<ul>
<li>Make source available</li>
<li>License it under GPL</li>
<li>Include the full GPL license text</li>
</ul>
<p><strong>For games:</strong> Using GPL assets in a distributed game is complex. The game itself may need to be GPL. Consult a lawyer if unsure.</p>
<p><strong>Recommendation:</strong> Avoid GPL-licensed assets for commercial games unless you fully understand the implications.</p>
<hr>
<h2 id="practical-guidelines" tabindex="-1">Practical Guidelines <a class="header-anchor" href="#practical-guidelines" aria-label="Permalink to &quot;Practical Guidelines&quot;"></a></h2>
<h3 id="choosing-assets-by-license-recommended-order" tabindex="-1">Choosing Assets by License (Recommended Order) <a class="header-anchor" href="#choosing-assets-by-license-recommended-order" aria-label="Permalink to &quot;Choosing Assets by License (Recommended Order)&quot;"></a></h3>
<ol>
<li><strong>CC0</strong> - No restrictions, no worries</li>
<li><strong>MIT</strong> - Include license text, you're good</li>
<li><strong>Royalty-Free</strong> - Standard commercial terms, read the specific license</li>
<li><strong>CC-BY</strong> - Must attribute, but otherwise free</li>
<li><strong>CC-BY-SA</strong> - Attribution + ShareAlike, fine for most games</li>
<li><strong>CC-BY-NC</strong> - Only if your project is 100% non-commercial</li>
<li><strong>GPL</strong> - Avoid unless you want your game to be open source</li>
<li><strong>Editorial Only</strong> - Never use in games</li>
</ol>
<h3 id="building-a-credits-system" tabindex="-1">Building a Credits System <a class="header-anchor" href="#building-a-credits-system" aria-label="Permalink to &quot;Building a Credits System&quot;"></a></h3>
<p>Even for CC0 assets, maintaining credits is good practice:</p>
<p><strong>1. Create a CREDITS.md file:</strong></p>
<div class="language-markdown vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">markdown</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold"># Asset Credits</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold">## 3D Models</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Spaceship: Kenney (https://kenney.nl) - CC0</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Tree Pack: Quaternius (https://quaternius.com) - CC0</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Robot Character: "RoboHelper" by @artist123 on Sketchfab - CC-BY 4.0</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  Modified: Reduced polygon count, changed colors</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold">## Textures</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Wood Floor: Poly Haven (https://polyhaven.com/a/wood_floor_01) - CC0</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Metal Panel: AmbientCG - CC0</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold">## Audio</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Laser Sound: "pew_laser" by sounduser on Freesound - CC-BY 3.0</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Background Music: Composed by [Your Name] - All Rights Reserved</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold">## Code/Shaders</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Soft Shadows: pmndrs/vanilla - MIT License</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">-</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Post-processing: Three.js Examples - MIT License</span></span></code></pre>
</div><p><strong>2. Add an in-game credits screen:</strong></p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>CREDITS</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Models &#x26; Textures</span></span>
<span class="line"><span>- Poly Haven (polyhaven.com)</span></span>
<span class="line"><span>- Kenney (kenney.nl)</span></span>
<span class="line"><span>- Quaternius (quaternius.com)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Audio</span></span>
<span class="line"><span>- Freesound contributors (see CREDITS.txt)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Special Thanks</span></span>
<span class="line"><span>- Three.js team</span></span>
<span class="line"><span>- Open source community</span></span></code></pre>
</div><h3 id="handling-mixed-licenses" tabindex="-1">Handling Mixed Licenses <a class="header-anchor" href="#handling-mixed-licenses" aria-label="Permalink to &quot;Handling Mixed Licenses&quot;"></a></h3>
<p>When your project uses assets with different licenses:</p>
<ol>
<li><strong>Track everything</strong> - Maintain a spreadsheet or document</li>
<li><strong>Use the most restrictive attribution</strong> - If any asset needs credit, have a credits section</li>
<li><strong>Separate concerns</strong> - Keep NC assets out of commercial projects</li>
<li><strong>When in doubt, replace</strong> - Swap questionable assets for clearly licensed ones</li>
</ol>
<h3 id="red-flags-to-watch-for" tabindex="-1">Red Flags to Watch For <a class="header-anchor" href="#red-flags-to-watch-for" aria-label="Permalink to &quot;Red Flags to Watch For&quot;"></a></h3>
<p>⚠️ <strong>Be cautious when:</strong></p>
<ul>
<li>License isn't clearly stated</li>
<li>Asset looks too professional to be free (might be pirated)</li>
<li>Asset is a recognizable character/brand</li>
<li>Site looks sketchy or unofficial</li>
<li>Terms say &quot;personal use only&quot;</li>
<li>Asset is ripped from a game</li>
</ul>
<p>✅ <strong>Trust indicators:</strong></p>
<ul>
<li>Established platforms (Poly Haven, Kenney, Sketchfab)</li>
<li>Clear license on download page</li>
<li>Creator has a portfolio/history</li>
<li>Asset is original work, not a copy</li>
</ul>
<hr>
<h2 id="license-compatibility" tabindex="-1">License Compatibility <a class="header-anchor" href="#license-compatibility" aria-label="Permalink to &quot;License Compatibility&quot;"></a></h2>
<p>Not all licenses can be combined. Here's what works together:</p>
<h3 id="combining-assets-in-one-project" tabindex="-1">Combining Assets in One Project <a class="header-anchor" href="#combining-assets-in-one-project" aria-label="Permalink to &quot;Combining Assets in One Project&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>License A</th>
<th>+</th>
<th>License B</th>
<th>=</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<td>CC0</td>
<td>+</td>
<td>Anything</td>
<td>=</td>
<td>✅ Fine</td>
</tr>
<tr>
<td>CC-BY</td>
<td>+</td>
<td>CC-BY</td>
<td>=</td>
<td>✅ Credit both</td>
</tr>
<tr>
<td>CC-BY</td>
<td>+</td>
<td>CC-BY-SA</td>
<td>=</td>
<td>✅ Credit both, SA applies to that asset</td>
</tr>
<tr>
<td>CC-BY</td>
<td>+</td>
<td>CC-BY-NC</td>
<td>=</td>
<td>⚠️ Project becomes NC</td>
</tr>
<tr>
<td>CC-BY-SA</td>
<td>+</td>
<td>CC-BY-NC-SA</td>
<td>=</td>
<td>⚠️ Incompatible for derivatives</td>
</tr>
<tr>
<td>MIT</td>
<td>+</td>
<td>CC-BY</td>
<td>=</td>
<td>✅ Include MIT license, credit CC-BY</td>
</tr>
<tr>
<td>GPL</td>
<td>+</td>
<td>Proprietary</td>
<td>=</td>
<td>❌ Problematic</td>
</tr>
</tbody>
</table>
<h3 id="key-rule" tabindex="-1">Key Rule <a class="header-anchor" href="#key-rule" aria-label="Permalink to &quot;Key Rule&quot;"></a></h3>
<p>When combining licenses, the most restrictive terms apply to the final work.</p>
<hr>
<h2 id="frequently-asked-questions" tabindex="-1">Frequently Asked Questions <a class="header-anchor" href="#frequently-asked-questions" aria-label="Permalink to &quot;Frequently Asked Questions&quot;"></a></h2>
<h3 id="can-i-use-cc-by-assets-in-a-commercial-game" tabindex="-1">Can I use CC-BY assets in a commercial game? <a class="header-anchor" href="#can-i-use-cc-by-assets-in-a-commercial-game" aria-label="Permalink to &quot;Can I use CC-BY assets in a commercial game?&quot;"></a></h3>
<p><strong>Yes.</strong> CC-BY allows commercial use. Just include attribution.</p>
<h3 id="do-i-need-to-open-source-my-game-if-i-use-cc-by-sa-assets" tabindex="-1">Do I need to open-source my game if I use CC-BY-SA assets? <a class="header-anchor" href="#do-i-need-to-open-source-my-game-if-i-use-cc-by-sa-assets" aria-label="Permalink to &quot;Do I need to open-source my game if I use CC-BY-SA assets?&quot;"></a></h3>
<p><strong>No.</strong> Your game code can be proprietary. Only the asset (and modifications to it) must remain CC-BY-SA if distributed separately.</p>
<h3 id="what-if-i-modified-a-cc-by-asset-beyond-recognition" tabindex="-1">What if I modified a CC-BY asset beyond recognition? <a class="header-anchor" href="#what-if-i-modified-a-cc-by-asset-beyond-recognition" aria-label="Permalink to &quot;What if I modified a CC-BY asset beyond recognition?&quot;"></a></h3>
<p><strong>Still attribute.</strong> If you started with someone's work, you should credit them regardless of how much you changed it. It's both legally safer and ethically right.</p>
<h3 id="can-i-use-freesound-audio-in-a-commercial-game" tabindex="-1">Can I use Freesound audio in a commercial game? <a class="header-anchor" href="#can-i-use-freesound-audio-in-a-commercial-game" aria-label="Permalink to &quot;Can I use Freesound audio in a commercial game?&quot;"></a></h3>
<p><strong>Depends on the specific sound.</strong> Each sound on Freesound has its own license. Filter by CC0 for worry-free use, or CC-BY if you can attribute.</p>
<h3 id="is-a-free-game-with-ads-commercial" tabindex="-1">Is a free game with ads &quot;commercial&quot;? <a class="header-anchor" href="#is-a-free-game-with-ads-commercial" aria-label="Permalink to &quot;Is a free game with ads &quot;commercial&quot;?&quot;"></a></h3>
<p><strong>Yes.</strong> Any monetization (ads, IAP, donations, Patreon) generally counts as commercial. Avoid NC-licensed assets.</p>
<h3 id="can-i-use-cc0-assets-and-then-sell-them" tabindex="-1">Can I use CC0 assets and then sell them? <a class="header-anchor" href="#can-i-use-cc0-assets-and-then-sell-them" aria-label="Permalink to &quot;Can I use CC0 assets and then sell them?&quot;"></a></h3>
<p><strong>Technically yes, but...</strong> You can include CC0 assets in a product you sell. You cannot claim you created them if asked. Reselling unchanged CC0 assets is legal but ethically questionable.</p>
<h3 id="what-about-ai-generated-assets" tabindex="-1">What about AI-generated assets? <a class="header-anchor" href="#what-about-ai-generated-assets" aria-label="Permalink to &quot;What about AI-generated assets?&quot;"></a></h3>
<p><strong>Complicated.</strong> Current legal consensus is unclear. Some considerations:</p>
<ul>
<li>AI-generated images may not be copyrightable (no human author)</li>
<li>Training data licensing is legally contested</li>
<li>Some marketplaces (CGTrader) have &quot;No AI&quot; restrictions</li>
<li>When in doubt, use human-created assets with clear licenses</li>
</ul>
<p>On copyrightability specifically, the US Copyright Office's January 2025 report on AI is the clearest guidance to date: a work generated entirely by AI is not copyrightable because copyright requires human authorship, and prompts alone (even long, detailed ones) don't make the output yours. But AI used as a tool, where a human meaningfully selects, arranges, or modifies the result, can produce a copyrightable work, judged case by case. So an AI-generated texture you drop in untouched likely has no copyright protection you can enforce, while a scene you substantially build and edit around AI elements can.</p>
<h3 id="do-i-need-a-lawyer" tabindex="-1">Do I need a lawyer? <a class="header-anchor" href="#do-i-need-a-lawyer" aria-label="Permalink to &quot;Do I need a lawyer?&quot;"></a></h3>
<p><strong>For most indie games, no.</strong> If you stick to CC0, MIT, and standard Royalty-Free licenses from reputable sources, you're fine.</p>
<p><strong>Consider legal advice if:</strong></p>
<ul>
<li>Your game has significant revenue potential</li>
<li>You're using GPL-licensed assets</li>
<li>You're unsure about a specific license</li>
<li>You're creating an asset marketplace</li>
</ul>
<hr>
<h2 id="attribution-templates" tabindex="-1">Attribution Templates <a class="header-anchor" href="#attribution-templates" aria-label="Permalink to &quot;Attribution Templates&quot;"></a></h2>
<p>Copy and customize these for your projects.</p>
<h3 id="simple-credits-file" tabindex="-1">Simple Credits File <a class="header-anchor" href="#simple-credits-file" aria-label="Permalink to &quot;Simple Credits File&quot;"></a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>ASSET CREDITS</span></span>
<span class="line"><span></span></span>
<span class="line"><span>This game uses assets from the following sources:</span></span>
<span class="line"><span></span></span>
<span class="line"><span>3D Models</span></span>
<span class="line"><span>- Kenney.nl (CC0) - https://kenney.nl</span></span>
<span class="line"><span>- Quaternius (CC0) - https://quaternius.com</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Textures  </span></span>
<span class="line"><span>- Poly Haven (CC0) - https://polyhaven.com</span></span>
<span class="line"><span>- AmbientCG (CC0) - https://ambientcg.com</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Audio</span></span>
<span class="line"><span>- Freesound.org (various CC licenses)</span></span>
<span class="line"><span>  - "explosion.wav" by user123 (CC-BY 3.0)</span></span>
<span class="line"><span>  - "footstep.wav" by user456 (CC0)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Thank you to all creators who share their work!</span></span></code></pre>
</div><h3 id="detailed-attribution-cc-by" tabindex="-1">Detailed Attribution (CC-BY) <a class="header-anchor" href="#detailed-attribution-cc-by" aria-label="Permalink to &quot;Detailed Attribution (CC-BY)&quot;"></a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>"Asset Name" by Creator Name</span></span>
<span class="line"><span>Source: https://example.com/asset-url</span></span>
<span class="line"><span>License: Creative Commons Attribution 4.0 International (CC-BY 4.0)</span></span>
<span class="line"><span>License URL: https://creativecommons.org/licenses/by/4.0/</span></span>
<span class="line"><span>Modifications: [Describe any changes made, or "None"]</span></span></code></pre>
</div><h3 id="mit-license-inclusion" tabindex="-1">MIT License Inclusion <a class="header-anchor" href="#mit-license-inclusion" aria-label="Permalink to &quot;MIT License Inclusion&quot;"></a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>This software includes code/assets licensed under the MIT License:</span></span>
<span class="line"><span></span></span>
<span class="line"><span>---</span></span>
<span class="line"><span>[Original copyright and license text]</span></span>
<span class="line"><span>---</span></span></code></pre>
</div><hr>
<h2 id="summary" tabindex="-1">Summary <a class="header-anchor" href="#summary" aria-label="Permalink to &quot;Summary&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>If You Want...</th>
<th>Use These Licenses</th>
</tr>
</thead>
<tbody>
<tr>
<td>No restrictions at all</td>
<td>CC0</td>
</tr>
<tr>
<td>Commercial use, minimal hassle</td>
<td>CC0, Royalty-Free, MIT</td>
</tr>
<tr>
<td>Commercial use, willing to credit</td>
<td>CC-BY</td>
</tr>
<tr>
<td>Free hobby project only</td>
<td>Any except Editorial</td>
</tr>
<tr>
<td>Maximum legal safety</td>
<td>CC0 from reputable sources</td>
</tr>
</tbody>
</table>
<p><strong>Golden rules:</strong></p>
<ol>
<li>When in doubt, use CC0</li>
<li>Always check the license before downloading</li>
<li>Keep records of where assets came from</li>
<li>Include a credits file even if not required</li>
<li>Replace questionable assets rather than risk issues</li>
</ol>
<hr>
<h2 id="resources" tabindex="-1">Resources <a class="header-anchor" href="#resources" aria-label="Permalink to &quot;Resources&quot;"></a></h2>
<ul>
<li><a href="https://creativecommons.org/choose/" target="_blank" rel="noreferrer">Creative Commons License Chooser</a></li>
<li><a href="https://tldrlegal.com/" target="_blank" rel="noreferrer">TL;DR Legal</a> — plain-English license explanations</li>
<li><a href="https://choosealicense.com/" target="_blank" rel="noreferrer">Choose a License</a> — for code licenses</li>
</ul>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> — providers, formats, and search tools for every asset type</li>
<li><a href="/guides/game-dev-courses.html">Online Game Development Courses</a> — courses that teach asset creation in Blender and other tools</li>
<li><a href="/guides/game-jams-hackathons.html">Game Jams &amp; Hackathons</a> — license-friendly asset packs for jam games</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Where to Find Free Game Assets in 2026 (20+ Sources)]]></title>
            <link>https://app.cinevva.com/guides/game-assets-guide</link>
            <guid>https://app.cinevva.com/guides/game-assets-guide</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[20+ sources for free game assets in 2026 - 3D models, textures, audio, sprites, animations, and shaders, with licenses explained and what each site is best for.]]></description>
            <content:encoded><![CDATA[<h1 id="where-to-find-free-game-assets-in-2026-20-sources" tabindex="-1">Where to Find Free Game Assets in 2026 (20+ Sources) <a class="header-anchor" href="#where-to-find-free-game-assets-in-2026-20-sources" aria-label="Permalink to &quot;Where to Find Free Game Assets in 2026 (20+ Sources)&quot;"></a></h1>
<p><em>Last updated: June 2026.</em></p>
<p>Where to find high-quality, free game assets for your web games: 3D models, textures, sprites, audio, music, animations, and shaders. Most of these providers are searchable directly inside Cinevva's asset library (look for the &quot;Searchable in Cinevva&quot; note), and we've included the best external sources too.</p>
<div class="info custom-block"><p class="custom-block-title">Try Our Asset Search</p>
<p>Search across all these sources at once with our <a href="/assets.html">Asset Library Search</a>. Find 3D models, textures, audio, and more from a single search box.</p>
<p><strong>New:</strong> Assets with direct download links show a download icon on the tile. Click to download instantly without leaving the search results.</p>
<p><strong>Quick search by source:</strong> <a href="/assets.html?providers=polyhaven">Poly Haven</a> · <a href="/assets.html?providers=sketchfab">Sketchfab</a> · <a href="/assets.html?providers=freesound">Freesound</a> · <a href="/assets.html?providers=kenney">Kenney</a> · <a href="/assets.html?providers=ambientcg">AmbientCG</a></p>
</div>
<div class="tip custom-block"><p class="custom-block-title">Understanding Licenses</p>
<p>Not sure what CC0, CC-BY, or Royalty-Free mean? Read our <a href="/guides/game-asset-licenses.html">Game Asset Licenses Explained</a> guide for a complete breakdown.</p>
</div>
<h2 id="quick-reference" tabindex="-1">Quick Reference <a class="header-anchor" href="#quick-reference" aria-label="Permalink to &quot;Quick Reference&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Need</th>
<th>Best Provider</th>
<th>License</th>
<th>Direct Download</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>3D Models (high-quality)</strong></td>
<td>Poly Haven, Sketchfab, BlenderKit</td>
<td>CC0 / Various</td>
<td>✅ / Manual</td>
</tr>
<tr>
<td><strong>3D Models (game-ready)</strong></td>
<td>Kenney, Quaternius</td>
<td>CC0</td>
<td>✅</td>
</tr>
<tr>
<td><strong>3D Models (stylized low-poly)</strong></td>
<td>Synty POLYGON, Quaternius</td>
<td>Royalty-Free / CC0</td>
<td>✅</td>
</tr>
<tr>
<td><strong>3D Models (marketplace)</strong></td>
<td>CGTrader, TurboSquid</td>
<td>Royalty-Free</td>
<td>✅</td>
</tr>
<tr>
<td><strong>Textures (PBR)</strong></td>
<td>Poly Haven, AmbientCG, BlenderKit</td>
<td>CC0 / Various</td>
<td>✅</td>
</tr>
<tr>
<td><strong>Sprites/2D</strong></td>
<td>Kenney, itch.io, OpenGameArt</td>
<td>CC0 / Various</td>
<td>✅</td>
</tr>
<tr>
<td><strong>Sound Effects</strong></td>
<td>Freesound, Sonniss GDC, Kenney</td>
<td>Various / Royalty-Free / CC0</td>
<td>⚠️ / ✅</td>
</tr>
<tr>
<td><strong>Music</strong></td>
<td>Jamendo</td>
<td>Creative Commons</td>
<td>✅</td>
</tr>
<tr>
<td><strong>Character Animations</strong></td>
<td>Mixamo</td>
<td>Free commercial</td>
<td>Manual</td>
</tr>
<tr>
<td><strong>Shaders/Effects</strong></td>
<td>Shadertoy, Three.js Examples</td>
<td>Various / MIT</td>
<td>View code</td>
</tr>
<tr>
<td><strong>Blender Models</strong></td>
<td>BlendSwap</td>
<td>CC-BY</td>
<td>Manual</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="_3d-models" tabindex="-1">3D Models <a class="header-anchor" href="#_3d-models" aria-label="Permalink to &quot;3D Models&quot;"></a></h2>
<h3 id="poly-haven" tabindex="-1">Poly Haven <a class="header-anchor" href="#poly-haven" aria-label="Permalink to &quot;Poly Haven&quot;"></a></h3>
<p><strong>Best for:</strong> High-quality photorealistic models, HDRIs, PBR textures</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://polyhaven.com" target="_blank" rel="noreferrer">polyhaven.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>CC0 (Public Domain) - no attribution required</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>1,500+ models, textures, HDRIs</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>GLTF, Blend, FBX</td>
</tr>
<tr>
<td><strong>API</strong></td>
<td>Yes - no key required</td>
</tr>
<tr>
<td><strong>Direct Download</strong></td>
<td>✅ Yes - 1K resolution via CDN</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Professional quality suitable for commercial projects</li>
<li>All assets are CC0 - use them however you want</li>
<li>Multiple resolutions available (1K to 16K for textures)</li>
<li>Direct download links via API - download icon in search results</li>
</ul>
<p><strong>Best for finding:</strong></p>
<ul>
<li>Architectural elements (furniture, buildings)</li>
<li>Natural objects (rocks, plants, terrain)</li>
<li>HDRIs for lighting</li>
<li>PBR material textures</li>
</ul>
<hr>
<h3 id="kenney" tabindex="-1">Kenney <a class="header-anchor" href="#kenney" aria-label="Permalink to &quot;Kenney&quot;"></a></h3>
<p><strong>Best for:</strong> Game-ready low-poly assets in consistent art styles</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://kenney.nl" target="_blank" rel="noreferrer">kenney.nl</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>CC0 (Public Domain)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>201 packs (~40,000+ individual assets)</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>GLB, PNG, WAV, and more</td>
</tr>
<tr>
<td><strong>Catalog</strong></td>
<td>Offline catalog available (instant search)</td>
</tr>
<tr>
<td><strong>Direct Download</strong></td>
<td>✅ Yes - links to asset pack page</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Massive library of game-focused assets</li>
<li>Consistent art styles within packs</li>
<li>Everything is game-ready, optimized for performance</li>
<li>Includes models, sprites, UI elements, and sounds in one place</li>
</ul>
<p><strong>Best packs to start with:</strong></p>
<ul>
<li><strong>Space Kit</strong> - Spaceships, asteroids, stations</li>
<li><strong>Platformer Kit</strong> - Characters, tiles, obstacles</li>
<li><strong>Nature Kit</strong> - Trees, rocks, grass</li>
<li><strong>Furniture Kit</strong> - Indoor props</li>
<li><strong>UI Pack</strong> - Buttons, icons, HUD elements</li>
</ul>
<hr>
<h3 id="quaternius" tabindex="-1">Quaternius <a class="header-anchor" href="#quaternius" aria-label="Permalink to &quot;Quaternius&quot;"></a></h3>
<p><strong>Best for:</strong> Stylized low-poly 3D models</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://quaternius.com" target="_blank" rel="noreferrer">quaternius.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>CC0 (Public Domain)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>79 packs (~1,148 models)</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>GLTF/GLB, FBX</td>
</tr>
<tr>
<td><strong>Catalog</strong></td>
<td>Offline catalog available</td>
</tr>
<tr>
<td><strong>Direct Download</strong></td>
<td>✅ Yes - ZIP file per pack</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Cute, stylized low-poly aesthetic</li>
<li>Great for indie games</li>
<li>Well-organized themed packs</li>
<li>Optimized polygon counts</li>
</ul>
<p><strong>Popular packs:</strong></p>
<ul>
<li>Ultimate Modular Characters</li>
<li>Medieval Pack</li>
<li>Ultimate Spaceships</li>
<li>Ultimate Monsters</li>
<li>Nature Pack</li>
</ul>
<hr>
<h3 id="sketchfab" tabindex="-1">Sketchfab <a class="header-anchor" href="#sketchfab" aria-label="Permalink to &quot;Sketchfab&quot;"></a></h3>
<p><strong>Best for:</strong> Wide variety of models, including complex/detailed ones</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://sketchfab.com" target="_blank" rel="noreferrer">sketchfab.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Various (check each model)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>4M+ models</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>GLTF, FBX, OBJ</td>
</tr>
<tr>
<td><strong>API</strong></td>
<td>Yes - optional key for higher limits</td>
</tr>
<tr>
<td><strong>Direct Download</strong></td>
<td>⚠️ Links to download page (requires login)</td>
</tr>
</tbody>
</table>
<p><strong>Note:</strong> Sketchfab's paid Store has closed and moved to Epic's <a href="https://www.fab.com" target="_blank" rel="noreferrer">Fab</a> marketplace, but free and view-only models stay on Sketchfab and remain downloadable, and the download/viewer APIs keep working. Filtering for free, downloadable models works exactly as before.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Largest 3D model library</li>
<li>3D preview before download</li>
<li>Filter by &quot;downloadable&quot; and license</li>
<li>Many professional artists</li>
</ul>
<p><strong>Search tips:</strong></p>
<ul>
<li>Add <code>downloadable:true</code> to search</li>
<li>Filter by CC0 or CC-BY licenses</li>
<li>Check polygon count for web games (&lt; 100K preferred)</li>
<li>Look for &quot;game-ready&quot; tag</li>
</ul>
<div class="tip custom-block"><p class="custom-block-title">Searchable in Cinevva</p>
<p>Sketchfab results show up directly in the Cinevva Assets panel.</p>
</div>
<hr>
<h3 id="blenderkit" tabindex="-1">BlenderKit <a class="header-anchor" href="#blenderkit" aria-label="Permalink to &quot;BlenderKit&quot;"></a></h3>
<p><strong>Best for:</strong> A huge in-app library of models, materials, and HDRIs</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://www.blenderkit.com" target="_blank" rel="noreferrer">blenderkit.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Various (CC0, Royalty-Free, CC-BY, GPL) - check each asset</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Thousands of models, materials, HDRIs, and brushes</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>Blend, GLTF, FBX (varies)</td>
</tr>
<tr>
<td><strong>API</strong></td>
<td>Yes - free tier available</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>One of the largest asset libraries with a mix of free and paid content</li>
<li>Covers models, PBR materials, and HDRIs in one place</li>
<li>Strong for environment props, materials, and scene dressing</li>
<li>Filterable by license so you can stick to CC0 when you need to</li>
</ul>
<p><strong>Note:</strong> Free and paid assets sit side by side, so filter for the free or CC0 tier when budget matters.</p>
<div class="tip custom-block"><p class="custom-block-title">Searchable in Cinevva</p>
<p>BlenderKit is integrated into the Cinevva Assets panel - search it alongside Poly Haven and Sketchfab without leaving the editor.</p>
</div>
<hr>
<h3 id="fab-epic-games" tabindex="-1">Fab (Epic Games) <a class="header-anchor" href="#fab-epic-games" aria-label="Permalink to &quot;Fab (Epic Games)&quot;"></a></h3>
<p><strong>Best for:</strong> Game-ready assets and free Quixel Megascans, all in one marketplace</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://www.fab.com" target="_blank" rel="noreferrer">fab.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Standard (Personal/Professional) and Creative Commons (some CC0) - check each listing</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Models, materials, environments, VFX, audio, animations, plugins</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>Unreal Engine, Unity, glTF, and more</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>Free sign-up, mix of free and paid</td>
</tr>
</tbody>
</table>
<p>Fab is Epic's unified content marketplace, the successor to the Unreal Engine Marketplace, ArtStation Marketplace, the Sketchfab Store, and Quixel Megascans. The big draw for asset hunters is a free Megascans starter pack of more than 1,500 hand-picked assets, plus surfaces, decals, and 3D scans, free to use in any engine. Filter by the Creative Commons or free Standard license to stay budget-friendly, and check the license on each listing before shipping.</p>
<hr>
<h3 id="blendswap" tabindex="-1">BlendSwap <a class="header-anchor" href="#blendswap" aria-label="Permalink to &quot;BlendSwap&quot;"></a></h3>
<p><strong>Best for:</strong> Complex Blender models with materials</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://blendswap.com" target="_blank" rel="noreferrer">blendswap.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Various (CC-BY common)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Thousands of Blender models</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>Blend (convert to GLTF)</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Community-driven quality content</li>
<li>Full Blender files with materials</li>
<li>Good for learning/modifying</li>
<li>Many architectural and vehicle models</li>
</ul>
<p><strong>Note:</strong> Usually requires export to GLTF for web use.</p>
<hr>
<h3 id="synty-studios-polygon" tabindex="-1">Synty Studios (POLYGON) <a class="header-anchor" href="#synty-studios-polygon" aria-label="Permalink to &quot;Synty Studios (POLYGON)&quot;"></a></h3>
<p><strong>Best for:</strong> Professional stylized low-poly assets with consistent art style</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://syntystore.com" target="_blank" rel="noreferrer">syntystore.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Royalty-Free (5 seats per purchase)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>100+ themed packs</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>FBX, Unity, Unreal</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>Paid (with free starter packs)</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Industry-standard stylized low-poly aesthetic</li>
<li>Consistent art style across ALL packs (mix and match!)</li>
<li>Professional quality, optimized for games</li>
<li>Characters have Mecanim rigs (Unity)</li>
</ul>
<p><strong>Free packs available:</strong></p>
<ul>
<li><strong>POLYGON Starter Pack</strong> - Sample of props, characters, environment pieces</li>
<li><strong>POLYGON Santa Pack</strong> - Holiday-themed assets</li>
<li><strong>POLYGON Easter Pack</strong> - Seasonal decorations</li>
<li><strong>POLYGON Xmas Pack</strong> - Christmas/winter assets</li>
</ul>
<p><strong>Popular paid packs:</strong></p>
<ul>
<li>POLYGON City, Town, Fantasy, Sci-Fi</li>
<li>POLYGON Nature, Farm, Prototype</li>
<li>POLYGON Knights, Samurai, Pirates</li>
</ul>
<p><strong>Best for:</strong></p>
<ul>
<li>Indie games needing cohesive art direction</li>
<li>Rapid prototyping with polished look</li>
<li>Mobile and web games (optimized poly counts)</li>
</ul>
<p><strong>Note:</strong> Free packs are great for testing the style. Paid packs are excellent value for the quantity and quality.</p>
<hr>
<h3 id="cgtrader" tabindex="-1">CGTrader <a class="header-anchor" href="#cgtrader" aria-label="Permalink to &quot;CGTrader&quot;"></a></h3>
<p><strong>Best for:</strong> Large marketplace with many free models</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://cgtrader.com" target="_blank" rel="noreferrer">cgtrader.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Royalty-Free (per model)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>1M+ models (many free)</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>FBX, OBJ, GLTF, Blend</td>
</tr>
<tr>
<td><strong>API</strong></td>
<td>No</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Large variety from professional artists</li>
<li>Many free models available</li>
<li>Filter by price and format</li>
<li>Both low-poly and high-detail models</li>
</ul>
<p><strong>License terms:</strong></p>
<ul>
<li>✅ Use in commercial games/apps</li>
<li>✅ Modify and adapt</li>
<li>❌ Cannot resell the model files</li>
<li>❌ Cannot allow users to extract model files</li>
</ul>
<p><strong>Search tips:</strong></p>
<ul>
<li>Filter by &quot;Free&quot; in price</li>
<li>Check polygon count</li>
<li>Look for &quot;game-ready&quot; or &quot;low-poly&quot; tags</li>
<li>Verify format compatibility (GLTF preferred for web)</li>
</ul>
<p><strong>Categories:</strong></p>
<ul>
<li>Vehicles, Characters, Architecture</li>
<li>Weapons, Furniture, Nature</li>
<li>Sci-Fi, Fantasy, Modern</li>
</ul>
<hr>
<h3 id="turbosquid" tabindex="-1">TurboSquid <a class="header-anchor" href="#turbosquid" aria-label="Permalink to &quot;TurboSquid&quot;"></a></h3>
<p><strong>Best for:</strong> Professional 3D model marketplace</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://turbosquid.com" target="_blank" rel="noreferrer">turbosquid.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Royalty-Free / Editorial (check each)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>1M+ models (many free)</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>FBX, OBJ, MAX, Blend</td>
</tr>
<tr>
<td><strong>Quality</strong></td>
<td>Professional grade</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>High-quality professional models</li>
<li>Established marketplace (since 2000)</li>
<li>CheckMate certification for quality</li>
<li>Many free models available</li>
</ul>
<p><strong>License terms:</strong></p>
<ul>
<li>✅ Commercial use allowed (Royalty-Free)</li>
<li>⚠️ Check for &quot;Editorial Only&quot; label (restricted use)</li>
<li>⚠️ Check for Depicted IP (branded items)</li>
<li>❌ Cannot redistribute raw model files</li>
</ul>
<p><strong>Cautions:</strong></p>
<ul>
<li>&quot;Editorial Only&quot; models cannot be used commercially</li>
<li>Models with brand logos need additional clearance</li>
<li>Free models may have less indemnification protection</li>
</ul>
<p><strong>Search tips:</strong></p>
<ul>
<li>Filter by &quot;Free&quot; and desired format</li>
<li>Avoid &quot;Editorial Use Only&quot; for commercial projects</li>
<li>Check polygon count for web games</li>
<li>Prefer models without recognizable brand IP</li>
</ul>
<hr>
<h3 id="poly-ronin" tabindex="-1">Poly Ronin <a class="header-anchor" href="#poly-ronin" aria-label="Permalink to &quot;Poly Ronin&quot;"></a></h3>
<p><strong>Best for:</strong> Stylized low-poly themed packs</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://polyronin.com" target="_blank" rel="noreferrer">polyronin.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>⚠️ Not clearly stated</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Themed low-poly packs</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>FBX, GLB, OBJ</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>Free</td>
</tr>
</tbody>
</table>
<p><strong>Available packs:</strong></p>
<ul>
<li>Samurai Weapons</li>
<li>Viking Weapons</li>
<li>Desert Village props</li>
<li>Market stalls</li>
<li>City vehicles</li>
</ul>
<p><strong>Caution:</strong></p>
<ul>
<li>License terms are NOT clearly documented</li>
<li>No explicit commercial use permission found</li>
<li>Contact creator before commercial use</li>
<li>Fine for prototyping and personal projects</li>
</ul>
<p><strong>Recommendation:</strong> Use for prototyping, but verify license before shipping a commercial game. Consider alternatives with clear licensing (Kenney, Quaternius) for production.</p>
<hr>
<h2 id="textures-materials" tabindex="-1">Textures &amp; Materials <a class="header-anchor" href="#textures-materials" aria-label="Permalink to &quot;Textures &amp; Materials&quot;"></a></h2>
<h3 id="poly-haven-textures" tabindex="-1">Poly Haven (Textures) <a class="header-anchor" href="#poly-haven-textures" aria-label="Permalink to &quot;Poly Haven (Textures)&quot;"></a></h3>
<p><strong>Best for:</strong> Photorealistic PBR textures</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://polyhaven.com/textures" target="_blank" rel="noreferrer">polyhaven.com/textures</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>CC0</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>900+ PBR texture sets</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>JPG, PNG, EXR</td>
</tr>
</tbody>
</table>
<p><strong>Includes:</strong></p>
<ul>
<li>Diffuse/Albedo maps</li>
<li>Normal maps</li>
<li>Roughness maps</li>
<li>Displacement maps</li>
<li>AO maps</li>
</ul>
<p><strong>Common categories:</strong></p>
<ul>
<li>Wood, Metal, Stone, Concrete</li>
<li>Fabric, Leather, Ground</li>
<li>Brick, Tile, Organic</li>
</ul>
<hr>
<h3 id="ambientcg" tabindex="-1">AmbientCG <a class="header-anchor" href="#ambientcg" aria-label="Permalink to &quot;AmbientCG&quot;"></a></h3>
<p><strong>Best for:</strong> Large variety of CC0 PBR materials</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://ambientcg.com" target="_blank" rel="noreferrer">ambientcg.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>CC0</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>1,700+ materials</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>JPG, PNG</td>
</tr>
<tr>
<td><strong>API</strong></td>
<td>Yes - no key required</td>
</tr>
<tr>
<td><strong>Direct Download</strong></td>
<td>✅ Yes - 1K resolution ZIP</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Similar quality to Poly Haven</li>
<li>More variety in some categories</li>
<li>Seamless tiling textures</li>
<li>Multiple resolutions (1K-8K)</li>
</ul>
<p><strong>Good for:</strong></p>
<ul>
<li>Ground textures (grass, dirt, gravel)</li>
<li>Building materials</li>
<li>Fabric and upholstery</li>
<li>Metallic surfaces</li>
</ul>
<hr>
<h2 id="sprites-2d-art" tabindex="-1">Sprites &amp; 2D Art <a class="header-anchor" href="#sprites-2d-art" aria-label="Permalink to &quot;Sprites &amp; 2D Art&quot;"></a></h2>
<h3 id="kenney-2d" tabindex="-1">Kenney (2D) <a class="header-anchor" href="#kenney-2d" aria-label="Permalink to &quot;Kenney (2D)&quot;"></a></h3>
<p><strong>Best for:</strong> Complete game UI and sprite sets</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>License</strong></td>
<td>CC0</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>100+ 2D packs</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>PNG, SVG</td>
</tr>
</tbody>
</table>
<p><strong>Great 2D packs:</strong></p>
<ul>
<li>UI Pack (buttons, windows, icons)</li>
<li>Platformer Pack (characters, tiles)</li>
<li>Roguelike Pack (dungeon tiles)</li>
<li>Space Shooter Pack</li>
<li>Puzzle Pack</li>
</ul>
<hr>
<h3 id="itch-io" tabindex="-1">itch.io <a class="header-anchor" href="#itch-io" aria-label="Permalink to &quot;itch.io&quot;"></a></h3>
<p><strong>Best for:</strong> Unique art styles from indie creators</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://itch.io/game-assets" target="_blank" rel="noreferrer">itch.io/game-assets</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Various (check each)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>43,000+ asset packs</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>Various</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Incredible variety of art styles</li>
<li>Many free packs available</li>
<li>Support indie artists</li>
<li>Filter by &quot;Free&quot; and type</li>
</ul>
<p><strong>Search tips:</strong></p>
<ul>
<li>Filter by price: Free</li>
<li>Popular tags: pixel-art, sprites, tileset, characters</li>
<li>Check license terms carefully</li>
</ul>
<hr>
<h3 id="opengameart" tabindex="-1">OpenGameArt <a class="header-anchor" href="#opengameart" aria-label="Permalink to &quot;OpenGameArt&quot;"></a></h3>
<p><strong>Best for:</strong> Community-contributed 2D art</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://opengameart.org" target="_blank" rel="noreferrer">opengameart.org</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Various (CC0, CC-BY, GPL)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Thousands</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>PNG, SVG</td>
</tr>
<tr>
<td><strong>Direct Download</strong></td>
<td>⚠️ Links to content page</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Active community</li>
<li>Good for retro/pixel art</li>
<li>Many complete game art sets</li>
<li>Also has audio and 3D</li>
</ul>
<p><strong>Categories:</strong></p>
<ul>
<li>2D Art &gt; Tiles, Characters, UI</li>
<li>Textures</li>
<li>Concept Art</li>
</ul>
<hr>
<h2 id="audio-sound-effects" tabindex="-1">Audio &amp; Sound Effects <a class="header-anchor" href="#audio-sound-effects" aria-label="Permalink to &quot;Audio &amp; Sound Effects&quot;"></a></h2>
<h3 id="freesound" tabindex="-1">Freesound <a class="header-anchor" href="#freesound" aria-label="Permalink to &quot;Freesound&quot;"></a></h3>
<p><strong>Best for:</strong> Massive sound effects library</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://freesound.org" target="_blank" rel="noreferrer">freesound.org</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Various CC licenses</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>600,000+ sounds</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>WAV, MP3, FLAC</td>
</tr>
<tr>
<td><strong>API</strong></td>
<td>Yes - requires free API key</td>
</tr>
<tr>
<td><strong>Direct Download</strong></td>
<td>✅ Yes - MP3 preview (with API key)</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Largest free sound library</li>
<li>Advanced search filters</li>
<li>Community ratings</li>
<li>Waveform previews</li>
</ul>
<p><strong>Tips:</strong></p>
<ul>
<li>Check license (CC0 preferred for games)</li>
<li>Filter by sample rate (44.1kHz standard)</li>
<li>Many variations of common sounds</li>
<li>Great for sound design</li>
</ul>
<p><strong>Common searches:</strong></p>
<ul>
<li>Footsteps, explosions, UI clicks</li>
<li>Ambient loops, background noise</li>
<li>Voice/vocal effects</li>
<li>Musical instruments</li>
</ul>
<hr>
<h3 id="kenney-audio" tabindex="-1">Kenney (Audio) <a class="header-anchor" href="#kenney-audio" aria-label="Permalink to &quot;Kenney (Audio)&quot;"></a></h3>
<p><strong>Best for:</strong> Game-ready sound effects packs</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>License</strong></td>
<td>CC0</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>15+ audio packs</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>WAV, OGG</td>
</tr>
</tbody>
</table>
<p><strong>Packs include:</strong></p>
<ul>
<li>UI Audio (clicks, hovers, notifications)</li>
<li>RPG Audio (combat, spells, environment)</li>
<li>Sci-Fi Audio (weapons, computers, doors)</li>
<li>Impact Sounds</li>
<li>Music Jingles</li>
</ul>
<hr>
<h3 id="sonniss-gdc" tabindex="-1">Sonniss GDC <a class="header-anchor" href="#sonniss-gdc" aria-label="Permalink to &quot;Sonniss GDC&quot;"></a></h3>
<p><strong>Best for:</strong> Professional, royalty-free sound effects in bulk</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://sonniss.com/gameaudiogdc" target="_blank" rel="noreferrer">sonniss.com/gameaudiogdc</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Royalty-free for commercial use (Sonniss GDC Game Audio Bundle)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Hundreds of gigabytes of pro sound effects</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>WAV (full quality), MP3/OGG previews</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Studio-grade sound effects recorded by professional sound designers</li>
<li>Royalty-free, so no attribution and no per-use fees for commercial games</li>
<li>Released every year as part of the GDC Game Audio Bundle</li>
<li>Covers foley, ambiences, weapons, creatures, vehicles, and UI</li>
</ul>
<p><strong>Note:</strong> Previews stream as MP3/OGG inside Cinevva; full-resolution WAVs download from Sonniss.</p>
<div class="tip custom-block"><p class="custom-block-title">Searchable in Cinevva</p>
<p>Sonniss is integrated into the Cinevva Assets panel - search the GDC bundle right next to Freesound.</p>
</div>
<hr>
<h2 id="music" tabindex="-1">Music <a class="header-anchor" href="#music" aria-label="Permalink to &quot;Music&quot;"></a></h2>
<h3 id="jamendo" tabindex="-1">Jamendo <a class="header-anchor" href="#jamendo" aria-label="Permalink to &quot;Jamendo&quot;"></a></h3>
<p><strong>Best for:</strong> Free background music and soundtracks</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://www.jamendo.com" target="_blank" rel="noreferrer">jamendo.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Creative Commons (CC-BY-SA, CC-BY-NC-SA) - check each track</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>Hundreds of thousands of independent music tracks</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>MP3</td>
</tr>
<tr>
<td><strong>API</strong></td>
<td>Yes - requires a free client ID</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Huge catalog of independent music across every genre</li>
<li>Creative Commons tracks are free to use with attribution</li>
<li>Good for menus, level themes, ambience, and trailers</li>
<li>Searchable by genre, mood, and tempo</li>
</ul>
<p><strong>Note:</strong> Some tracks need a Jamendo Licensing purchase for commercial use, so confirm the license on each track before shipping.</p>
<div class="tip custom-block"><p class="custom-block-title">Searchable in Cinevva</p>
<p>Jamendo is integrated into the Cinevva Assets panel for background music and soundtracks.</p>
</div>
<hr>
<h2 id="character-animations" tabindex="-1">Character Animations <a class="header-anchor" href="#character-animations" aria-label="Permalink to &quot;Character Animations&quot;"></a></h2>
<h3 id="mixamo" tabindex="-1">Mixamo <a class="header-anchor" href="#mixamo" aria-label="Permalink to &quot;Mixamo&quot;"></a></h3>
<p><strong>Best for:</strong> Professional character animations</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://mixamo.com" target="_blank" rel="noreferrer">mixamo.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Free for commercial use</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>2,446 animations</td>
</tr>
<tr>
<td><strong>Formats</strong></td>
<td>FBX</td>
</tr>
<tr>
<td><strong>Account</strong></td>
<td>Adobe account required</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Professional quality animations</li>
<li>Retargetable to custom characters</li>
<li>Huge animation library</li>
<li>Also has rigged characters</li>
</ul>
<p><strong>Categories:</strong></p>
<ul>
<li>Locomotion (walk, run, jump)</li>
<li>Combat (punch, kick, sword)</li>
<li>Dance</li>
<li>Gestures</li>
<li>Actions (sit, climb, grab)</li>
<li>Reactions (hit, death, fall)</li>
</ul>
<p><strong>Workflow:</strong></p>
<ol>
<li>Create free Adobe account</li>
<li>Upload your character or use theirs</li>
<li>Browse/preview animations</li>
<li>Download as FBX</li>
<li>Convert to GLTF for web</li>
</ol>
<hr>
<h2 id="shaders-effects" tabindex="-1">Shaders &amp; Effects <a class="header-anchor" href="#shaders-effects" aria-label="Permalink to &quot;Shaders &amp; Effects&quot;"></a></h2>
<h3 id="shadertoy" tabindex="-1">Shadertoy <a class="header-anchor" href="#shadertoy" aria-label="Permalink to &quot;Shadertoy&quot;"></a></h3>
<p><strong>Best for:</strong> GLSL shader inspiration and effects</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://shadertoy.com" target="_blank" rel="noreferrer">shadertoy.com</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>Various (check each)</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>70,000+ shaders</td>
</tr>
<tr>
<td><strong>Format</strong></td>
<td>GLSL source code</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Mind-blowing visual effects</li>
<li>Learn shader techniques</li>
<li>Real-time preview</li>
<li>Active community</li>
</ul>
<p><strong>Popular categories:</strong></p>
<ul>
<li>Raymarching/fractals</li>
<li>Post-processing effects</li>
<li>Procedural textures</li>
<li>Particle systems</li>
<li>Water/fire/smoke</li>
</ul>
<p><strong>Note:</strong> Shaders need adaptation for Three.js/WebGL use.</p>
<hr>
<h3 id="three-js-examples" tabindex="-1">Three.js Examples <a class="header-anchor" href="#three-js-examples" aria-label="Permalink to &quot;Three.js Examples&quot;"></a></h3>
<p><strong>Best for:</strong> Ready-to-use Three.js code</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://threejs.org/examples" target="_blank" rel="noreferrer">threejs.org/examples</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>MIT</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>500+ examples</td>
</tr>
<tr>
<td><strong>Format</strong></td>
<td>JavaScript source</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>Official examples</li>
<li>Copy-paste ready</li>
<li>Well documented</li>
<li>Cover all features</li>
</ul>
<p><strong>Useful examples:</strong></p>
<ul>
<li>Post-processing effects</li>
<li>Custom shaders</li>
<li>Physics integration</li>
<li>Model loaders</li>
<li>Animation systems</li>
</ul>
<hr>
<h3 id="pmndrs-vanilla" tabindex="-1">pmndrs/vanilla <a class="header-anchor" href="#pmndrs-vanilla" aria-label="Permalink to &quot;pmndrs/vanilla&quot;"></a></h3>
<p><strong>Best for:</strong> Three.js materials and effects</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Website</strong></td>
<td><a href="https://github.com/pmndrs/vanilla" target="_blank" rel="noreferrer">github.com/pmndrs/vanilla</a></td>
</tr>
<tr>
<td><strong>License</strong></td>
<td>MIT</td>
</tr>
<tr>
<td><strong>Assets</strong></td>
<td>20+ effects</td>
</tr>
<tr>
<td><strong>Package</strong></td>
<td><code>@pmndrs/vanilla</code></td>
</tr>
</tbody>
</table>
<p><strong>Includes:</strong></p>
<ul>
<li>MeshReflectorMaterial</li>
<li>MeshDistortMaterial</li>
<li>MeshWobbleMaterial</li>
<li>Caustics</li>
<li>Sparkles</li>
<li>Soft shadows</li>
</ul>
<hr>
<h2 id="asset-type-cheat-sheet" tabindex="-1">Asset Type Cheat Sheet <a class="header-anchor" href="#asset-type-cheat-sheet" aria-label="Permalink to &quot;Asset Type Cheat Sheet&quot;"></a></h2>
<h3 id="i-need-a-spaceship" tabindex="-1">I need a spaceship <a class="header-anchor" href="#i-need-a-spaceship" aria-label="Permalink to &quot;I need a spaceship&quot;"></a></h3>
<ol>
<li><strong>Quick &amp; easy:</strong> Kenney Space Kit (CC0)</li>
<li><strong>Stylized:</strong> Quaternius Ultimate Spaceships (CC0)</li>
<li><strong>Professional stylized:</strong> Synty POLYGON Sci-Fi (Royalty-Free, paid)</li>
<li><strong>Detailed:</strong> Sketchfab search &quot;spaceship downloadable&quot; (check license)</li>
</ol>
<h3 id="i-need-a-consistent-art-style-for-my-whole-game" tabindex="-1">I need a consistent art style for my whole game <a class="header-anchor" href="#i-need-a-consistent-art-style-for-my-whole-game" aria-label="Permalink to &quot;I need a consistent art style for my whole game&quot;"></a></h3>
<ol>
<li><strong>Synty POLYGON</strong> - All packs share the same aesthetic, mix and match freely</li>
<li><strong>Kenney</strong> - CC0, cohesive game-ready style within packs</li>
<li><strong>Quaternius</strong> - CC0, cute low-poly style</li>
</ol>
<h3 id="i-need-realistic-textures" tabindex="-1">I need realistic textures <a class="header-anchor" href="#i-need-realistic-textures" aria-label="Permalink to &quot;I need realistic textures&quot;"></a></h3>
<ol>
<li><strong>Poly Haven</strong> - Best quality, CC0</li>
<li><strong>AmbientCG</strong> - More variety, CC0</li>
</ol>
<h3 id="i-need-sound-effects" tabindex="-1">I need sound effects <a class="header-anchor" href="#i-need-sound-effects" aria-label="Permalink to &quot;I need sound effects&quot;"></a></h3>
<ol>
<li><strong>Kenney Audio Packs</strong> - Quick game sounds, CC0</li>
<li><strong>Freesound</strong> - Specific sounds, various licenses</li>
</ol>
<h3 id="i-need-character-animations" tabindex="-1">I need character animations <a class="header-anchor" href="#i-need-character-animations" aria-label="Permalink to &quot;I need character animations&quot;"></a></h3>
<ol>
<li><strong>Mixamo</strong> - Professional quality, requires account</li>
</ol>
<h3 id="i-need-pixel-art" tabindex="-1">I need pixel art <a class="header-anchor" href="#i-need-pixel-art" aria-label="Permalink to &quot;I need pixel art&quot;"></a></h3>
<ol>
<li><strong>itch.io</strong> - Filter by &quot;pixel-art&quot; and &quot;Free&quot;</li>
<li><strong>OpenGameArt</strong> - 2D Art category</li>
<li><strong>Kenney</strong> - 2D packs</li>
</ol>
<h3 id="i-need-ui-elements" tabindex="-1">I need UI elements <a class="header-anchor" href="#i-need-ui-elements" aria-label="Permalink to &quot;I need UI elements&quot;"></a></h3>
<ol>
<li><strong>Kenney UI Pack</strong> - Complete set, CC0</li>
<li><strong>itch.io</strong> - Search &quot;UI&quot; or &quot;GUI&quot;</li>
</ol>
<h3 id="i-need-a-wide-variety-of-random-models" tabindex="-1">I need a wide variety of random models <a class="header-anchor" href="#i-need-a-wide-variety-of-random-models" aria-label="Permalink to &quot;I need a wide variety of random models&quot;"></a></h3>
<ol>
<li><strong>CGTrader</strong> - Filter by &quot;Free&quot;, huge variety</li>
<li><strong>TurboSquid</strong> - Professional quality, check license type</li>
<li><strong>Sketchfab</strong> - 4M+ models, filter by downloadable</li>
</ol>
<hr>
<h2 id="license-guide" tabindex="-1">License Guide <a class="header-anchor" href="#license-guide" aria-label="Permalink to &quot;License Guide&quot;"></a></h2>
<h3 id="cc0-public-domain" tabindex="-1">CC0 (Public Domain) <a class="header-anchor" href="#cc0-public-domain" aria-label="Permalink to &quot;CC0 (Public Domain)&quot;"></a></h3>
<ul>
<li>✅ Use commercially</li>
<li>✅ Modify</li>
<li>✅ No attribution needed</li>
<li>✅ No restrictions</li>
</ul>
<p><strong>Providers:</strong> Poly Haven, Kenney, Quaternius, AmbientCG</p>
<h3 id="royalty-free-license" tabindex="-1">Royalty-Free License <a class="header-anchor" href="#royalty-free-license" aria-label="Permalink to &quot;Royalty-Free License&quot;"></a></h3>
<ul>
<li>✅ Use commercially (in your games/apps)</li>
<li>✅ Modify and adapt</li>
<li>✅ No ongoing royalties</li>
<li>❌ Cannot resell the raw model files</li>
<li>❌ Cannot let users extract the model</li>
</ul>
<p><strong>Providers:</strong> Synty POLYGON, CGTrader, TurboSquid</p>
<p><strong>Note:</strong> &quot;Royalty-Free&quot; means you pay once (or get it free) and can use it without paying per-use fees. It does NOT mean the asset is public domain.</p>
<h3 id="cc-by-attribution" tabindex="-1">CC-BY (Attribution) <a class="header-anchor" href="#cc-by-attribution" aria-label="Permalink to &quot;CC-BY (Attribution)&quot;"></a></h3>
<ul>
<li>✅ Use commercially</li>
<li>✅ Modify</li>
<li>⚠️ Must credit the author</li>
<li>✅ Otherwise unrestricted</li>
</ul>
<p><strong>Providers:</strong> Many Sketchfab, BlendSwap, OpenGameArt</p>
<h3 id="cc-by-sa-sharealike" tabindex="-1">CC-BY-SA (ShareAlike) <a class="header-anchor" href="#cc-by-sa-sharealike" aria-label="Permalink to &quot;CC-BY-SA (ShareAlike)&quot;"></a></h3>
<ul>
<li>✅ Use commercially</li>
<li>✅ Modify</li>
<li>⚠️ Must credit author</li>
<li>⚠️ Derivatives must use same license</li>
</ul>
<h3 id="cc-by-nc-non-commercial" tabindex="-1">CC-BY-NC (Non-Commercial) <a class="header-anchor" href="#cc-by-nc-non-commercial" aria-label="Permalink to &quot;CC-BY-NC (Non-Commercial)&quot;"></a></h3>
<ul>
<li>❌ Cannot use commercially</li>
<li>✅ Can use in free games</li>
<li>⚠️ Check if your game has ads/IAP</li>
</ul>
<h3 id="editorial-use-only" tabindex="-1">Editorial Use Only <a class="header-anchor" href="#editorial-use-only" aria-label="Permalink to &quot;Editorial Use Only&quot;"></a></h3>
<ul>
<li>❌ Cannot use commercially</li>
<li>✅ News, education, commentary only</li>
<li>⚠️ Common on TurboSquid for branded items</li>
</ul>
<p><strong>Tip:</strong> When possible, prefer CC0 to avoid attribution requirements in your game credits. Royalty-Free is also safe for commercial games.</p>
<hr>
<h2 id="workflow-tips" tabindex="-1">Workflow Tips <a class="header-anchor" href="#workflow-tips" aria-label="Permalink to &quot;Workflow Tips&quot;"></a></h2>
<h3 id="_1-check-the-license-first" tabindex="-1">1. Check the license first <a class="header-anchor" href="#_1-check-the-license-first" aria-label="Permalink to &quot;1. Check the license first&quot;"></a></h3>
<p>Before downloading, verify the license allows your intended use.</p>
<h3 id="_2-optimize-for-web" tabindex="-1">2. Optimize for web <a class="header-anchor" href="#_2-optimize-for-web" aria-label="Permalink to &quot;2. Optimize for web&quot;"></a></h3>
<ul>
<li>Keep models under 100K polygons</li>
<li>Use compressed textures (JPG for diffuse, PNG for transparency)</li>
<li>Audio: Use MP3 or OGG, not WAV for production</li>
</ul>
<h3 id="_3-organize-your-assets" tabindex="-1">3. Organize your assets <a class="header-anchor" href="#_3-organize-your-assets" aria-label="Permalink to &quot;3. Organize your assets&quot;"></a></h3>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>assets/</span></span>
<span class="line"><span>├── models/</span></span>
<span class="line"><span>│   ├── characters/</span></span>
<span class="line"><span>│   └── props/</span></span>
<span class="line"><span>├── textures/</span></span>
<span class="line"><span>│   ├── environment/</span></span>
<span class="line"><span>│   └── ui/</span></span>
<span class="line"><span>├── audio/</span></span>
<span class="line"><span>│   ├── music/</span></span>
<span class="line"><span>│   └── sfx/</span></span>
<span class="line"><span>└── sprites/</span></span></code></pre>
</div><h3 id="_4-keep-attribution-records" tabindex="-1">4. Keep attribution records <a class="header-anchor" href="#_4-keep-attribution-records" aria-label="Permalink to &quot;4. Keep attribution records&quot;"></a></h3>
<p>Even for CC0, it's nice to track where assets came from:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>CREDITS.md</span></span>
<span class="line"><span>- Spaceship model: Quaternius (CC0)</span></span>
<span class="line"><span>- Wood texture: Poly Haven (CC0)</span></span>
<span class="line"><span>- Laser sound: Freesound user "xyz" (CC-BY)</span></span></code></pre>
</div><h3 id="_5-test-performance" tabindex="-1">5. Test performance <a class="header-anchor" href="#_5-test-performance" aria-label="Permalink to &quot;5. Test performance&quot;"></a></h3>
<p>Load assets in your game early to check:</p>
<ul>
<li>File sizes (affects load time)</li>
<li>Polygon counts (affects FPS)</li>
<li>Texture resolutions (affects memory)</li>
</ul>
<hr>
<h2 id="summary-table" tabindex="-1">Summary Table <a class="header-anchor" href="#summary-table" aria-label="Permalink to &quot;Summary Table&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Provider</th>
<th>Types</th>
<th>License</th>
<th>Best For</th>
<th style="text-align:center">In Cinevva</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Poly Haven</strong></td>
<td>Models, Textures, HDRIs</td>
<td>CC0</td>
<td>Professional quality</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Kenney</strong></td>
<td>Models, Sprites, Audio, UI</td>
<td>CC0</td>
<td>Game-ready assets</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Quaternius</strong></td>
<td>Models</td>
<td>CC0</td>
<td>Stylized low-poly</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Synty POLYGON</strong></td>
<td>Models, Characters</td>
<td>Royalty-Free</td>
<td>Consistent stylized art</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Sketchfab</strong></td>
<td>Models</td>
<td>Various</td>
<td>Wide variety</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>BlenderKit</strong></td>
<td>Models, Materials, HDRIs</td>
<td>Various</td>
<td>Big in-app library</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Fab (Epic)</strong></td>
<td>Models, Materials, VFX, Audio</td>
<td>Standard / CC</td>
<td>Free Megascans, game-ready assets</td>
<td style="text-align:center">—</td>
</tr>
<tr>
<td><strong>CGTrader</strong></td>
<td>Models</td>
<td>Royalty-Free</td>
<td>Marketplace variety</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>AmbientCG</strong></td>
<td>Textures, HDRIs</td>
<td>CC0</td>
<td>PBR materials</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>OpenGameArt</strong></td>
<td>2D, Audio, Textures</td>
<td>Various</td>
<td>Community content</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Freesound</strong></td>
<td>Audio</td>
<td>Various</td>
<td>Sound effects</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Sonniss GDC</strong></td>
<td>Audio</td>
<td>Royalty-Free</td>
<td>Pro sound effects in bulk</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>Jamendo</strong></td>
<td>Music</td>
<td>Creative Commons</td>
<td>Background music</td>
<td style="text-align:center">✅</td>
</tr>
<tr>
<td><strong>TurboSquid</strong></td>
<td>Models</td>
<td>Royalty-Free</td>
<td>Professional quality</td>
<td style="text-align:center">—</td>
</tr>
<tr>
<td><strong>Mixamo</strong></td>
<td>Animations</td>
<td>Free</td>
<td>Character animations</td>
<td style="text-align:center">—</td>
</tr>
<tr>
<td><strong>itch.io</strong></td>
<td>Sprites, Various</td>
<td>Various</td>
<td>Indie art styles</td>
<td style="text-align:center">—</td>
</tr>
<tr>
<td><strong>BlendSwap</strong></td>
<td>Blender Models</td>
<td>Various</td>
<td>Complex models</td>
<td style="text-align:center">—</td>
</tr>
<tr>
<td><strong>Three.js Examples</strong></td>
<td>Code/Shaders</td>
<td>MIT</td>
<td>Three.js features</td>
<td style="text-align:center">—</td>
</tr>
<tr>
<td><strong>Shadertoy</strong></td>
<td>Shaders</td>
<td>Various</td>
<td>Visual effects</td>
<td style="text-align:center">—</td>
</tr>
</tbody>
</table>
<hr>
<p>The providers marked ✅ are searchable directly through Cinevva's integrated asset library. Open the Assets panel to search across all of them at once. The rest are external sources worth bookmarking for assets Cinevva doesn't index yet.</p>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="where-can-i-find-free-3d-models-for-games" tabindex="-1">Where can I find free 3D models for games? <a class="header-anchor" href="#where-can-i-find-free-3d-models-for-games" aria-label="Permalink to &quot;Where can I find free 3D models for games?&quot;"></a></h3>
<p>Poly Haven, Kenney, and Quaternius are the best starting points for free, game-ready 3D models, all under CC0 so you can use them commercially with no attribution. For more variety, Sketchfab and BlendSwap have huge libraries under mixed licenses. Always check the license on the individual model before shipping.</p>
<h3 id="are-free-game-assets-okay-to-use-in-commercial-games" tabindex="-1">Are free game assets okay to use in commercial games? <a class="header-anchor" href="#are-free-game-assets-okay-to-use-in-commercial-games" aria-label="Permalink to &quot;Are free game assets okay to use in commercial games?&quot;"></a></h3>
<p>Most are, but it depends on the license. CC0 assets (Poly Haven, Kenney, Quaternius, AmbientCG) are public domain and free for commercial use with no attribution. CC-BY requires crediting the creator. CC-BY-NC and Editorial-Only assets cannot be used in commercial games. The License Guide section above breaks down each one.</p>
<h3 id="what-is-the-best-site-for-free-game-sound-effects" tabindex="-1">What is the best site for free game sound effects? <a class="header-anchor" href="#what-is-the-best-site-for-free-game-sound-effects" aria-label="Permalink to &quot;What is the best site for free game sound effects?&quot;"></a></h3>
<p>Freesound has the largest library of free sound effects, though licenses vary per clip, so check each one. Kenney offers smaller but fully CC0 audio packs that are safe for any commercial project with zero attribution requirements.</p>
<h3 id="where-can-i-get-free-character-animations" tabindex="-1">Where can I get free character animations? <a class="header-anchor" href="#where-can-i-get-free-character-animations" aria-label="Permalink to &quot;Where can I get free character animations?&quot;"></a></h3>
<p>Mixamo is the go-to source for free character animations and auto-rigging. Upload or pick a character, choose from thousands of motion-capture animations, and export them ready for your engine, all free with an Adobe account.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/game-asset-licenses.html">Game Asset Licenses Explained</a> — understanding CC0, CC-BY, Royalty-Free, and other licenses</li>
<li><a href="/guides/game-dev-courses.html">Online Game Development Courses</a> — courses on 3D modeling in Blender and game art</li>
<li><a href="/tutorials/pixel-art-rendering.html">Pixel art rendering</a> — rendering sprite sheets and pixel art in the browser</li>
<li><a href="/tutorials/streaming-asset-loading.html">Streaming asset loading</a> — loading and caching game assets progressively</li>
<li><a href="/guides/threejs-usdc-tech-report.html">Three.js + USDC in the Browser</a> — loading USD 3D assets in Three.js</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Online Game Development Courses Guide]]></title>
            <link>https://app.cinevva.com/guides/game-dev-courses</link>
            <guid>https://app.cinevva.com/guides/game-dev-courses</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Online courses for game development — engines, design, art, and audio with credibility analysis, reviews, and pricing]]></description>
            <content:encoded><![CDATA[<h1 id="online-game-development-courses-guide" tabindex="-1">Online Game Development Courses Guide <a class="header-anchor" href="#online-game-development-courses-guide" aria-label="Permalink to &quot;Online Game Development Courses Guide&quot;"></a></h1>
<p>Online courses for learning game development. This guide covers engine-specific courses, game design theory, specialized skills (art, audio), and platform comparisons — with credibility assessments, student reviews, and pricing details.</p>
<div class="tip custom-block"><p class="custom-block-title">Looking for Free Assets Instead?</p>
<p>Check out our <a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> guide for 3D models, textures, audio, and more.</p>
</div>
<h2 id="quick-reference" tabindex="-1">Quick Reference <a class="header-anchor" href="#quick-reference" aria-label="Permalink to &quot;Quick Reference&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Goal</th>
<th>Best Option</th>
<th>Price Range</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Unity Beginner</strong></td>
<td>GameDev.tv Complete Unity 3D (Udemy)</td>
<td>$12–30 (sale)</td>
<td>⭐ 4.7/5</td>
</tr>
<tr>
<td><strong>Unity Formal Credential</strong></td>
<td>Michigan State Specialization (Coursera)</td>
<td>$49/month</td>
<td>⭐ 4.7/5</td>
</tr>
<tr>
<td><strong>Unreal Engine C++</strong></td>
<td>Stephen Ulibarri UE5 Course (Udemy)</td>
<td>$15–30 (sale)</td>
<td>⭐ 4.8/5</td>
</tr>
<tr>
<td><strong>Godot Engine</strong></td>
<td>GDQuest Bundles</td>
<td>$84–216</td>
<td>⭐ 4.8/5</td>
</tr>
<tr>
<td><strong>Game Design Theory</strong></td>
<td>Harvard CS50 Game Dev (archived, free)</td>
<td>Free</td>
<td>⭐ 4.6/5</td>
</tr>
<tr>
<td><strong>3D Modeling for Games</strong></td>
<td>Complete Blender Creator (Udemy)</td>
<td>$15–30 (sale)</td>
<td>⭐ 4.7/5</td>
</tr>
<tr>
<td><strong>Game Audio</strong></td>
<td>Berklee Game Audio Certificate</td>
<td>~$4,500</td>
<td>⭐ 4.8/5</td>
</tr>
<tr>
<td><strong>Free Official Training</strong></td>
<td>Unity Learn Pathways</td>
<td>Free</td>
<td>⭐ 4.5/5</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="game-engine-courses" tabindex="-1">Game Engine Courses <a class="header-anchor" href="#game-engine-courses" aria-label="Permalink to &quot;Game Engine Courses&quot;"></a></h2>
<h3 id="unity" tabindex="-1">Unity <a class="header-anchor" href="#unity" aria-label="Permalink to &quot;Unity&quot;"></a></h3>
<h4 id="gamedev-tv-complete-unity-developer-3d" tabindex="-1">GameDev.tv Complete Unity Developer 3D <a class="header-anchor" href="#gamedev-tv-complete-unity-developer-3d" aria-label="Permalink to &quot;GameDev.tv Complete Unity Developer 3D&quot;"></a></h4>
<p><strong>Best for:</strong> Beginners wanting hands-on project-based learning</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Udemy</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$139 list / <strong>$12–30 on sale</strong></td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~27 hours video + projects</td>
</tr>
<tr>
<td><strong>Rating</strong></td>
<td>⭐ 4.7/5 (460,000+ students)</td>
</tr>
<tr>
<td><strong>Last Updated</strong></td>
<td>Unity 6 (2025)</td>
</tr>
</tbody>
</table>
<p><strong>What You'll Build:</strong></p>
<ul>
<li>5 complete games from scratch</li>
<li>Fundamentals of C# programming</li>
<li>3D physics, AI, and game mechanics</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Excellent for absolute beginners</li>
<li>✅ Project-based learning (you build real games)</li>
<li>✅ Large community for Q&amp;A support</li>
<li>✅ Updated regularly for new Unity versions</li>
<li>✅ Lifetime access once purchased</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Some sections can feel slow-paced</li>
<li>⚠️ Full price is overpriced (always buy on sale)</li>
<li>⚠️ Less depth on advanced topics like optimization</li>
</ul>
<p><strong>Student Reviews:</strong></p>
<blockquote>
<p>&quot;Great course for beginners. I went from knowing nothing to having a portfolio of games.&quot; — Reddit r/unity</p>
</blockquote>
<blockquote>
<p>&quot;Instructor explains concepts clearly. The project approach keeps you motivated.&quot; — Udemy review</p>
</blockquote>
<p><strong>Credibility:</strong> GameDev.tv is one of the most established game development education providers. Founded by Ben Tristem and Rick Davidson, they've trained over 2 million students. They're transparent about retiring outdated courses and maintain active community forums.</p>
<hr>
<h4 id="michigan-state-game-design-and-development-with-unity-specialization" tabindex="-1">Michigan State Game Design and Development with Unity Specialization <a class="header-anchor" href="#michigan-state-game-design-and-development-with-unity-specialization" aria-label="Permalink to &quot;Michigan State Game Design and Development with Unity Specialization&quot;"></a></h4>
<p><strong>Best for:</strong> Those wanting university credentials and structured learning</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Coursera</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>Free to audit / <strong>$49/month for certificate</strong></td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~2 months (10 hrs/week)</td>
</tr>
<tr>
<td><strong>Rating</strong></td>
<td>⭐ 4.7/5</td>
</tr>
<tr>
<td><strong>Institution</strong></td>
<td>Michigan State University</td>
</tr>
</tbody>
</table>
<p><strong>Curriculum (five courses, built in Unity):</strong></p>
<ul>
<li>Game Design and Development 1: 2D Shooter</li>
<li>Game Design and Development 2: 2D Platformer</li>
<li>Game Design and Development 3: 3D Shooter</li>
<li>Game Design and Development 4: 3D Platformer</li>
<li>Capstone Project</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ University-backed credential</li>
<li>✅ Hands-on: you build five complete Unity game projects</li>
<li>✅ Can audit for free (no certificate)</li>
<li>✅ Rigorous academic approach</li>
<li>✅ Includes business/entrepreneurship module</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Slower paced than Udemy courses</li>
<li>⚠️ Less hands-on coding than other options</li>
<li>⚠️ Certificate requires paid subscription</li>
</ul>
<p><strong>Best For:</strong> Career changers who need formal credentials, or those who prefer structured academic learning.</p>
<hr>
<h4 id="unity-learn-pathways" tabindex="-1">Unity Learn Pathways <a class="header-anchor" href="#unity-learn-pathways" aria-label="Permalink to &quot;Unity Learn Pathways&quot;"></a></h4>
<p><strong>Best for:</strong> Budget learners wanting official Unity training</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Unity Learn (learn.unity.com)</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td><strong>Free</strong></td>
</tr>
<tr>
<td><strong>Pathways</strong></td>
<td>Junior Programmer, Creative Core, VR Development</td>
</tr>
<tr>
<td><strong>Format</strong></td>
<td>Self-paced tutorials + projects</td>
</tr>
</tbody>
</table>
<p><strong>Available Pathways:</strong></p>
<ul>
<li><strong>Junior Programmer</strong> — Complete beginner to job-ready</li>
<li><strong>Creative Core</strong> — Art and design focus</li>
<li><strong>Unity Essentials</strong> — Quick fundamentals overview</li>
<li><strong>VR Development</strong> — Virtual reality specialization</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Completely free</li>
<li>✅ Official Unity content (always up-to-date)</li>
<li>✅ Project-based with portfolio pieces</li>
<li>✅ Built-in progress tracking</li>
<li>✅ Unity certifications available</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Less engaging than video courses</li>
<li>⚠️ Requires more self-discipline</li>
<li>⚠️ Community support less active than paid courses</li>
</ul>
<hr>
<h3 id="unreal-engine" tabindex="-1">Unreal Engine <a class="header-anchor" href="#unreal-engine" aria-label="Permalink to &quot;Unreal Engine&quot;"></a></h3>
<h4 id="stephen-ulibarri-s-ue5-c-developer-course" tabindex="-1">Stephen Ulibarri's UE5 C++ Developer Course <a class="header-anchor" href="#stephen-ulibarri-s-ue5-c-developer-course" aria-label="Permalink to &quot;Stephen Ulibarri's UE5 C++ Developer Course&quot;"></a></h4>
<p><strong>Best for:</strong> Serious developers wanting C++ mastery</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Udemy</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$175 list / <strong>$15–30 on sale</strong></td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~50+ hours</td>
</tr>
<tr>
<td><strong>Rating</strong></td>
<td>⭐ 4.8/5</td>
</tr>
<tr>
<td><strong>Project</strong></td>
<td>Complete Action-RPG</td>
</tr>
</tbody>
</table>
<p><strong>What You'll Learn:</strong></p>
<ul>
<li>Unreal Engine 5 from scratch</li>
<li>C++ programming for games</li>
<li>Blueprints visual scripting</li>
<li>Advanced gameplay systems (combat, AI, inventory)</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Deep C++ coverage (rare for game dev courses)</li>
<li>✅ Builds a substantial portfolio project</li>
<li>✅ Well-paced for intermediate learners</li>
<li>✅ Covers both Blueprints AND C++</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Assumes some programming background</li>
<li>⚠️ Very long course (commitment required)</li>
<li>⚠️ UE versions change frequently</li>
</ul>
<p><strong>Student Reviews:</strong></p>
<blockquote>
<p>&quot;Best Unreal course I've found. Stephen explains the 'why' not just the 'what'.&quot; — Udemy</p>
</blockquote>
<blockquote>
<p>&quot;Finally understand C++ in Unreal after struggling with other resources.&quot; — Reddit r/unrealengine</p>
</blockquote>
<hr>
<h4 id="coursera-unreal-engine-for-beginners-specialization" tabindex="-1">Coursera Unreal Engine for Beginners Specialization <a class="header-anchor" href="#coursera-unreal-engine-for-beginners-specialization" aria-label="Permalink to &quot;Coursera Unreal Engine for Beginners Specialization&quot;"></a></h4>
<p><strong>Best for:</strong> Complete beginners wanting structured intro</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Coursera</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$199 (sale) / $399 regular</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>3 courses, ~4 weeks each</td>
</tr>
<tr>
<td><strong>Certificate</strong></td>
<td>Yes</td>
</tr>
</tbody>
</table>
<p><strong>Course Sequence:</strong></p>
<ol>
<li>World Building in Unreal</li>
<li>Characters &amp; Interiors</li>
<li>Cameras &amp; Production</li>
</ol>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Beginner-friendly (no coding required initially)</li>
<li>✅ Focuses on visual/creative aspects</li>
<li>✅ Good for artists entering game dev</li>
<li>✅ University-style certificate</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Less programming depth</li>
<li>⚠️ More expensive than Udemy alternatives</li>
<li>⚠️ Better for environment artists than programmers</li>
</ul>
<hr>
<h4 id="linkedin-learning-unreal-engine-5-essential-training" tabindex="-1">LinkedIn Learning: Unreal Engine 5 Essential Training <a class="header-anchor" href="#linkedin-learning-unreal-engine-5-essential-training" aria-label="Permalink to &quot;LinkedIn Learning: Unreal Engine 5 Essential Training&quot;"></a></h4>
<p><strong>Best for:</strong> Quick skill refreshers</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>LinkedIn Learning</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>Subscription (~$30/month) / Free trial available</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~3 hours</td>
</tr>
<tr>
<td><strong>Rating</strong></td>
<td>⭐ 4.8/5</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ High production quality</li>
<li>✅ Great for quick overviews</li>
<li>✅ Covers UE5.6 features</li>
<li>✅ Certificate adds to LinkedIn profile</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Too short for deep learning</li>
<li>⚠️ Subscription model (not lifetime access)</li>
<li>⚠️ Better as supplement than primary learning</li>
</ul>
<hr>
<h3 id="godot-engine" tabindex="-1">Godot Engine <a class="header-anchor" href="#godot-engine" aria-label="Permalink to &quot;Godot Engine&quot;"></a></h3>
<h4 id="gdquest-study-programs" tabindex="-1">GDQuest Study Programs <a class="header-anchor" href="#gdquest-study-programs" aria-label="Permalink to &quot;GDQuest Study Programs&quot;"></a></h4>
<p><strong>Best for:</strong> Serious Godot learners wanting structured training</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>school.gdquest.com</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$84–216 (bundles)</td>
</tr>
<tr>
<td><strong>Format</strong></td>
<td>Interactive courses + community</td>
</tr>
<tr>
<td><strong>Rating</strong></td>
<td>⭐ 4.8/5</td>
</tr>
</tbody>
</table>
<p><strong>Available Bundles:</strong></p>
<ul>
<li><strong>Starter Kit</strong> (~$84) — 2D and 3D basics</li>
<li><strong>From Zero to Pro</strong> (~$216) — Complete learning path</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Highest quality Godot-specific courses</li>
<li>✅ Interactive exercises (not just videos)</li>
<li>✅ Updated for Godot 4.x</li>
<li>✅ Strong Discord community</li>
<li>✅ Created by well-known Godot contributors</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ More expensive than Udemy alternatives</li>
<li>⚠️ Smaller community than Unity/Unreal courses</li>
</ul>
<p><strong>Credibility:</strong> GDQuest team are recognized Godot contributors and educators. They've created official Godot documentation and are respected in the community.</p>
<hr>
<h4 id="complete-godot-4-beginner-course-udemy" tabindex="-1">Complete Godot 4 Beginner Course (Udemy) <a class="header-anchor" href="#complete-godot-4-beginner-course-udemy" aria-label="Permalink to &quot;Complete Godot 4 Beginner Course (Udemy)&quot;"></a></h4>
<p><strong>Best for:</strong> Budget-conscious beginners</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Udemy</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$80–100 list / <strong>$15–25 on sale</strong></td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~20 hours</td>
</tr>
<tr>
<td><strong>Projects</strong></td>
<td>Space shooter, platformer, top-down zombie game</td>
</tr>
</tbody>
</table>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Affordable during sales</li>
<li>✅ Builds 3 complete games</li>
<li>✅ GDScript focused (Godot's native language)</li>
<li>✅ Good for total beginners</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Less depth than GDQuest</li>
<li>⚠️ Smaller support community</li>
</ul>
<hr>
<h4 id="godot-dev-academy" tabindex="-1">Godot Dev Academy <a class="header-anchor" href="#godot-dev-academy" aria-label="Permalink to &quot;Godot Dev Academy&quot;"></a></h4>
<p><strong>Best for:</strong> Ongoing learners wanting subscription access</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>godotdevacademy.com</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td><strong>$19.99/month</strong></td>
</tr>
<tr>
<td><strong>Access</strong></td>
<td>All courses included</td>
</tr>
</tbody>
</table>
<p><strong>Includes:</strong></p>
<ul>
<li>2D platformer courses</li>
<li>RPG development</li>
<li>Mobile game dev</li>
<li>Free beginner courses available</li>
</ul>
<p><strong>Best For:</strong> Those who plan to take multiple courses over time. Good value if you're committed to learning Godot long-term.</p>
<hr>
<h2 id="game-design-theory" tabindex="-1">Game Design Theory <a class="header-anchor" href="#game-design-theory" aria-label="Permalink to &quot;Game Design Theory&quot;"></a></h2>
<h3 id="harvard-cs50-s-introduction-to-game-development" tabindex="-1">Harvard CS50's Introduction to Game Development <a class="header-anchor" href="#harvard-cs50-s-introduction-to-game-development" aria-label="Permalink to &quot;Harvard CS50's Introduction to Game Development&quot;"></a></h3>
<p><strong>Best for:</strong> Foundational understanding of game programming concepts</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>cs50.harvard.edu/games (archived)</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td><strong>Free</strong></td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>12 weeks</td>
</tr>
<tr>
<td><strong>Status</strong></td>
<td>Archived (materials available, no new submissions)</td>
</tr>
</tbody>
</table>
<p><strong>What You'll Learn:</strong></p>
<ul>
<li>2D and 3D graphics fundamentals</li>
<li>Animation and sound design</li>
<li>Collision detection and physics</li>
<li>Multiple engines: LÖVE2D, Unity, Lua</li>
</ul>
<p><strong>Games You'll Clone:</strong></p>
<ul>
<li>Pong, Flappy Bird, Breakout</li>
<li>Super Mario Bros., Legend of Zelda</li>
<li>Angry Birds, Pokémon</li>
<li>3D helicopter game, Portal</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Harvard-quality instruction</li>
<li>✅ Completely free</li>
<li>✅ Excellent theory and fundamentals</li>
<li>✅ Multiple engine exposure</li>
<li>✅ All materials still accessible</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Course officially retired (June 2024)</li>
<li>⚠️ No new certificates or grading</li>
<li>⚠️ Some technology dated (older Unity versions)</li>
<li>⚠️ Requires self-direction</li>
</ul>
<p><strong>Note:</strong> Harvard has launched a new course, CS50's Introduction to 2D Game Development (CS50 2D), replacing the retired version. It focuses on 2D games built with Lua and the LOVE 2D framework (Pong, Flappy Bird, Breakout, Match 3, Super Mario Bros., Legend of Zelda, Angry Birds, Pokemon). Lectures air daily on YouTube starting April 21, 2026, and you can register for a certificate at cs50.edx.org/2d.</p>
<p><strong>Student Reviews:</strong></p>
<blockquote>
<p>&quot;One of the best introductions to game programming I've seen. Covers fundamentals that transfer to any engine.&quot; — Reddit r/cs50</p>
</blockquote>
<blockquote>
<p>&quot;Even without the certificate, the knowledge gained is worth it.&quot; — Course review</p>
</blockquote>
<hr>
<h3 id="west-virginia-university-m-a-in-game-design-online" tabindex="-1">West Virginia University M.A. in Game Design (Online) <a class="header-anchor" href="#west-virginia-university-m-a-in-game-design-online" aria-label="Permalink to &quot;West Virginia University M.A. in Game Design (Online)&quot;"></a></h3>
<p><strong>Best for:</strong> Formal graduate education</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>WVU Online</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>~$22,650 total</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>2 years (flexible)</td>
</tr>
<tr>
<td><strong>Format</strong></td>
<td>Fully asynchronous online</td>
</tr>
<tr>
<td><strong>Credential</strong></td>
<td>Master's Degree</td>
</tr>
</tbody>
</table>
<p><strong>Curriculum Highlights:</strong></p>
<ul>
<li>Game Theory (cultural and aesthetic analysis)</li>
<li>Game Narrative and Storytelling</li>
<li>Player Experience Design</li>
<li>Capstone game prototype</li>
</ul>
<p><strong>Best For:</strong> Those seeking academic credentials for teaching, research, or leadership positions in game development.</p>
<hr>
<h2 id="specialized-skills" tabindex="-1">Specialized Skills <a class="header-anchor" href="#specialized-skills" aria-label="Permalink to &quot;Specialized Skills&quot;"></a></h2>
<h3 id="_3d-modeling-for-games" tabindex="-1">3D Modeling for Games <a class="header-anchor" href="#_3d-modeling-for-games" aria-label="Permalink to &quot;3D Modeling for Games&quot;"></a></h3>
<h4 id="complete-blender-creator-gamedev-tv" tabindex="-1">Complete Blender Creator (GameDev.tv) <a class="header-anchor" href="#complete-blender-creator-gamedev-tv" aria-label="Permalink to &quot;Complete Blender Creator (GameDev.tv)&quot;"></a></h4>
<p><strong>Best for:</strong> Beginners learning Blender for game asset creation</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Udemy</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$140 list / <strong>$15–30 on sale</strong></td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~30 hours</td>
</tr>
<tr>
<td><strong>Rating</strong></td>
<td>⭐ 4.7/5 (200,000+ students)</td>
</tr>
<tr>
<td><strong>Updated</strong></td>
<td>Blender 4.4 (2025)</td>
</tr>
</tbody>
</table>
<p><strong>What You'll Learn:</strong></p>
<ul>
<li>Blender fundamentals</li>
<li>3D modeling techniques</li>
<li>UV unwrapping and texturing</li>
<li>Basic animation</li>
<li>Exporting to game engines</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Massive student community</li>
<li>✅ Project-based approach</li>
<li>✅ Regularly updated for new Blender versions</li>
<li>✅ Great for complete beginners</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Very general (not game-specific pipeline)</li>
<li>⚠️ Long course commitment</li>
</ul>
<hr>
<h4 id="blender-3d-modeling-for-games-—-complete-workflow-udemy" tabindex="-1">Blender 3D Modeling for Games — Complete Workflow (Udemy) <a class="header-anchor" href="#blender-3d-modeling-for-games-—-complete-workflow-udemy" aria-label="Permalink to &quot;Blender 3D Modeling for Games — Complete Workflow (Udemy)&quot;"></a></h4>
<p><strong>Best for:</strong> Game-specific asset pipeline</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Udemy</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$15–40 on sale</td>
</tr>
<tr>
<td><strong>Focus</strong></td>
<td>Game-ready asset workflow</td>
</tr>
</tbody>
</table>
<p><strong>What You'll Learn:</strong></p>
<ul>
<li>Block-out to final asset</li>
<li>Game-optimized topology</li>
<li>Texturing and materials</li>
<li>Engine export workflow</li>
</ul>
<p><strong>Best For:</strong> Those specifically targeting game asset creation rather than general Blender skills.</p>
<hr>
<h3 id="game-audio-sound-design" tabindex="-1">Game Audio &amp; Sound Design <a class="header-anchor" href="#game-audio-sound-design" aria-label="Permalink to &quot;Game Audio &amp; Sound Design&quot;"></a></h3>
<h4 id="berklee-online-game-audio-design-certificate" tabindex="-1">Berklee Online: Game Audio Design Certificate <a class="header-anchor" href="#berklee-online-game-audio-design-certificate" aria-label="Permalink to &quot;Berklee Online: Game Audio Design Certificate&quot;"></a></h4>
<p><strong>Best for:</strong> Professional-level game audio training</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Berklee Online</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$4,725 total (or $4,253 paid in full) / $1,575 per course, plus $175 registration</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>3 courses, 12 weeks each</td>
</tr>
<tr>
<td><strong>Start Date</strong></td>
<td>Term-based enrollment (next start June 29)</td>
</tr>
</tbody>
</table>
<p><strong>Curriculum:</strong></p>
<ul>
<li>Game Audio 101 — Fundamentals</li>
<li>Sound production and dialogue</li>
<li>Audio integration and interactive scoring with Wwise</li>
<li>Building a professional demo reel</li>
</ul>
<p>Berklee's certificate is now structured as three courses: Game Design Principles, Game Audio 101, and Game Audio Production with Wwise (9 credits total).</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Industry-recognized credential</li>
<li>✅ Learn industry-standard tools (Wwise, FMOD)</li>
<li>✅ Berklee reputation in music education</li>
<li>✅ Portfolio-building focus</li>
</ul>
<p><strong>Weaknesses:</strong></p>
<ul>
<li>⚠️ Expensive</li>
<li>⚠️ Time-intensive (professional commitment)</li>
</ul>
<p><strong>Best For:</strong> Those pursuing game audio as a career.</p>
<hr>
<h4 id="game-audio-essentials-udemy" tabindex="-1">Game Audio Essentials (Udemy) <a class="header-anchor" href="#game-audio-essentials-udemy" aria-label="Permalink to &quot;Game Audio Essentials (Udemy)&quot;"></a></h4>
<p><strong>Best for:</strong> Budget-friendly introduction to game audio</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Udemy</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$50–100 list / <strong>$15–25 on sale</strong></td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~10 hours</td>
</tr>
<tr>
<td><strong>Updated</strong></td>
<td>July 2025</td>
</tr>
</tbody>
</table>
<p><strong>What You'll Learn:</strong></p>
<ul>
<li>Foley and sound effect creation</li>
<li>Audio integration in Unity/Unreal/Godot</li>
<li>Looping and spatial audio</li>
<li>Practical sound design techniques</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>✅ Affordable</li>
<li>✅ Hands-on practical skills</li>
<li>✅ Covers multiple game engines</li>
<li>✅ Good for indie developers</li>
</ul>
<hr>
<h4 id="domestika-designing-video-game-soundtracks" tabindex="-1">Domestika: Designing Video Game Soundtracks <a class="header-anchor" href="#domestika-designing-video-game-soundtracks" aria-label="Permalink to &quot;Domestika: Designing Video Game Soundtracks&quot;"></a></h4>
<p><strong>Best for:</strong> Composers wanting to enter game music</p>
<table tabindex="0">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Platform</strong></td>
<td>Domestika</td>
</tr>
<tr>
<td><strong>Price</strong></td>
<td>$10–60 (varies with sales)</td>
</tr>
<tr>
<td><strong>Duration</strong></td>
<td>~3 hours</td>
</tr>
<tr>
<td><strong>Focus</strong></td>
<td>Music composition for games</td>
</tr>
</tbody>
</table>
<p><strong>Best For:</strong> Musicians wanting to transition into game soundtrack composition.</p>
<hr>
<h2 id="platform-links" tabindex="-1">Platform Links <a class="header-anchor" href="#platform-links" aria-label="Permalink to &quot;Platform Links&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Platform</th>
<th>URL</th>
<th>Best For</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Udemy</strong></td>
<td><a href="https://www.udemy.com/" target="_blank" rel="noreferrer">udemy.com</a></td>
<td>Affordable courses on sale, wide selection</td>
</tr>
<tr>
<td><strong>Coursera</strong></td>
<td><a href="https://www.coursera.org/" target="_blank" rel="noreferrer">coursera.org</a></td>
<td>University courses, formal credentials</td>
</tr>
<tr>
<td><strong>Unity Learn</strong></td>
<td><a href="https://learn.unity.com/" target="_blank" rel="noreferrer">learn.unity.com</a></td>
<td>Free official Unity training</td>
</tr>
<tr>
<td><strong>GDQuest</strong></td>
<td><a href="https://school.gdquest.com/" target="_blank" rel="noreferrer">school.gdquest.com</a></td>
<td>Godot-focused courses from core contributors</td>
</tr>
<tr>
<td><strong>Pluralsight</strong></td>
<td><a href="https://www.pluralsight.com/" target="_blank" rel="noreferrer">pluralsight.com</a></td>
<td>Technical depth, skill assessments</td>
</tr>
<tr>
<td><strong>Skillshare</strong></td>
<td><a href="https://www.skillshare.com/" target="_blank" rel="noreferrer">skillshare.com</a></td>
<td>Creative/design focus, affordable subscription</td>
</tr>
<tr>
<td><strong>Berklee Online</strong></td>
<td><a href="https://online.berklee.edu/" target="_blank" rel="noreferrer">online.berklee.edu</a></td>
<td>Professional game audio certification</td>
</tr>
<tr>
<td><strong>edX / Harvard CS50</strong></td>
<td><a href="https://cs50.harvard.edu/games/" target="_blank" rel="noreferrer">cs50.harvard.edu/games</a></td>
<td>Free game development fundamentals</td>
</tr>
<tr>
<td><strong>Domestika</strong></td>
<td><a href="https://www.domestika.org/" target="_blank" rel="noreferrer">domestika.org</a></td>
<td>Affordable creative courses</td>
</tr>
<tr>
<td><strong>LinkedIn Learning</strong></td>
<td><a href="https://www.linkedin.com/learning/" target="_blank" rel="noreferrer">linkedin.com/learning</a></td>
<td>Professional development, Unreal Engine</td>
</tr>
<tr>
<td><strong>Blender</strong></td>
<td><a href="https://www.blender.org/" target="_blank" rel="noreferrer">blender.org</a></td>
<td>Free 3D modeling software</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="platform-comparison" tabindex="-1">Platform Comparison <a class="header-anchor" href="#platform-comparison" aria-label="Permalink to &quot;Platform Comparison&quot;"></a></h2>
<h3 id="udemy" tabindex="-1"><a href="https://www.udemy.com/" target="_blank" rel="noreferrer">Udemy</a> <a class="header-anchor" href="#udemy" aria-label="Permalink to &quot;[Udemy](https://www.udemy.com/)&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Pros</th>
<th>Cons</th>
</tr>
</thead>
<tbody>
<tr>
<td>✅ Massive course selection</td>
<td>⚠️ Quality varies widely</td>
</tr>
<tr>
<td>✅ Lifetime access</td>
<td>⚠️ List prices are inflated</td>
</tr>
<tr>
<td>✅ Frequent sales ($10–30)</td>
<td>⚠️ Some courses become outdated</td>
</tr>
<tr>
<td>✅ 30-day refund policy</td>
<td>⚠️ No formal credentials</td>
</tr>
<tr>
<td>✅ Large Q&amp;A communities</td>
<td>⚠️ No live instruction</td>
</tr>
</tbody>
</table>
<p><strong>Buying Tip:</strong> NEVER pay full price on Udemy. Sales occur every few weeks with discounts of 80–90%.</p>
<hr>
<h3 id="coursera" tabindex="-1">Coursera <a class="header-anchor" href="#coursera" aria-label="Permalink to &quot;Coursera&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Pros</th>
<th>Cons</th>
</tr>
</thead>
<tbody>
<tr>
<td>✅ University partnerships</td>
<td>⚠️ Subscription model for certificates</td>
</tr>
<tr>
<td>✅ Formal credentials</td>
<td>⚠️ Slower paced</td>
</tr>
<tr>
<td>✅ Free audit option</td>
<td>⚠️ Less hands-on than Udemy</td>
</tr>
<tr>
<td>✅ Structured learning paths</td>
<td>⚠️ Can be more expensive</td>
</tr>
<tr>
<td>✅ Academic rigor</td>
<td></td>
</tr>
</tbody>
</table>
<p><strong>Best For:</strong> Those who need credentials for employment or prefer structured academic learning.</p>
<hr>
<h3 id="pluralsight" tabindex="-1">Pluralsight <a class="header-anchor" href="#pluralsight" aria-label="Permalink to &quot;Pluralsight&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Pros</th>
<th>Cons</th>
</tr>
</thead>
<tbody>
<tr>
<td>✅ Technical depth</td>
<td>⚠️ Subscription only (~$29/month)</td>
</tr>
<tr>
<td>✅ Learning paths with assessments</td>
<td>⚠️ Some outdated content</td>
</tr>
<tr>
<td>✅ Unity partnership</td>
<td>⚠️ Less beginner-friendly</td>
</tr>
<tr>
<td>✅ Skill assessments</td>
<td>⚠️ Less community interaction</td>
</tr>
</tbody>
</table>
<p><strong>Best For:</strong> Intermediate developers wanting technical depth and career-focused learning paths.</p>
<hr>
<h3 id="skillshare" tabindex="-1">Skillshare <a class="header-anchor" href="#skillshare" aria-label="Permalink to &quot;Skillshare&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Pros</th>
<th>Cons</th>
</tr>
</thead>
<tbody>
<tr>
<td>✅ Affordable (~$168/year)</td>
<td>⚠️ Variable course quality</td>
</tr>
<tr>
<td>✅ Creative/design focus</td>
<td>⚠️ Less technical depth</td>
</tr>
<tr>
<td>✅ Good for beginners</td>
<td>⚠️ No formal credentials</td>
</tr>
<tr>
<td>✅ Short, digestible courses</td>
<td>⚠️ Subscription frustrations reported</td>
</tr>
</tbody>
</table>
<p><strong>Best For:</strong> Hobbyists and creative-focused learners exploring multiple topics.</p>
<hr>
<h3 id="unity-learn" tabindex="-1">Unity Learn <a class="header-anchor" href="#unity-learn" aria-label="Permalink to &quot;Unity Learn&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Pros</th>
<th>Cons</th>
</tr>
</thead>
<tbody>
<tr>
<td>✅ Completely free</td>
<td>⚠️ Less engaging format</td>
</tr>
<tr>
<td>✅ Official and up-to-date</td>
<td>⚠️ Requires self-discipline</td>
</tr>
<tr>
<td>✅ Unity certification path</td>
<td>⚠️ Limited community support</td>
</tr>
<tr>
<td>✅ Project-based</td>
<td></td>
</tr>
</tbody>
</table>
<p><strong>Best For:</strong> Self-motivated learners on a budget wanting official Unity training.</p>
<hr>
<h2 id="instructor-credibility-guide" tabindex="-1">Instructor Credibility Guide <a class="header-anchor" href="#instructor-credibility-guide" aria-label="Permalink to &quot;Instructor Credibility Guide&quot;"></a></h2>
<h3 id="highly-regarded-instructors" tabindex="-1">Highly Regarded Instructors <a class="header-anchor" href="#highly-regarded-instructors" aria-label="Permalink to &quot;Highly Regarded Instructors&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Instructor</th>
<th>Platform</th>
<th>Specialty</th>
<th>Why Trusted</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Ben Tristem / GameDev.tv</strong></td>
<td>Udemy</td>
<td>Unity, Unreal, Blender</td>
<td>1M+ students, transparent about course updates</td>
</tr>
<tr>
<td><strong>Stephen Ulibarri</strong></td>
<td>Udemy</td>
<td>Unreal C++</td>
<td>Deep technical knowledge, excellent explanations</td>
</tr>
<tr>
<td><strong>GDQuest Team</strong></td>
<td>GDQuest</td>
<td>Godot</td>
<td>Official Godot contributors</td>
</tr>
<tr>
<td><strong>Michigan State Faculty</strong></td>
<td>Coursera</td>
<td>Game Design</td>
<td>Academic credentials, research background</td>
</tr>
<tr>
<td><strong>Berklee Faculty</strong></td>
<td>Berklee Online</td>
<td>Game Audio</td>
<td>Industry professionals, Berklee reputation</td>
</tr>
</tbody>
</table>
<h3 id="red-flags-to-watch-for" tabindex="-1">Red Flags to Watch For <a class="header-anchor" href="#red-flags-to-watch-for" aria-label="Permalink to &quot;Red Flags to Watch For&quot;"></a></h3>
<ul>
<li>❌ Courses not updated in 2+ years</li>
<li>❌ Low ratings with specific complaints about outdated content</li>
<li>❌ Instructors with no visible portfolio or credentials</li>
<li>❌ Aggressive marketing without substance</li>
<li>❌ Courses that promise &quot;complete mastery&quot; in unrealistic timeframes</li>
</ul>
<hr>
<h2 id="learning-path-recommendations" tabindex="-1">Learning Path Recommendations <a class="header-anchor" href="#learning-path-recommendations" aria-label="Permalink to &quot;Learning Path Recommendations&quot;"></a></h2>
<h3 id="complete-beginner-→-job-ready-unity" tabindex="-1">Complete Beginner → Job Ready (Unity) <a class="header-anchor" href="#complete-beginner-→-job-ready-unity" aria-label="Permalink to &quot;Complete Beginner → Job Ready (Unity)&quot;"></a></h3>
<ol>
<li><strong>Start:</strong> Unity Learn Junior Programmer Pathway (Free)</li>
<li><strong>Build Skills:</strong> GameDev.tv Complete Unity 3D ($20)</li>
<li><strong>Specialize:</strong> Choose focus area (2D, 3D, mobile)</li>
<li><strong>Portfolio:</strong> Build 3–5 complete games</li>
<li><strong>Credential (Optional):</strong> Unity Certification or Coursera Specialization</li>
</ol>
<p><strong>Estimated Cost:</strong> $20–150<br>
<strong>Timeframe:</strong> 6–12 months (part-time)</p>
<hr>
<h3 id="artist-→-game-developer" tabindex="-1">Artist → Game Developer <a class="header-anchor" href="#artist-→-game-developer" aria-label="Permalink to &quot;Artist → Game Developer&quot;"></a></h3>
<ol>
<li><strong>Start:</strong> Complete Blender Creator (Udemy) — $20</li>
<li><strong>Game Pipeline:</strong> Blender for Games workflow course — $20</li>
<li><strong>Engine Basics:</strong> Unity/Unreal beginner course — $20</li>
<li><strong>Integration:</strong> Learn to import and use assets in-engine</li>
</ol>
<p><strong>Estimated Cost:</strong> $60–100<br>
<strong>Timeframe:</strong> 4–8 months</p>
<hr>
<h3 id="programmer-→-game-developer" tabindex="-1">Programmer → Game Developer <a class="header-anchor" href="#programmer-→-game-developer" aria-label="Permalink to &quot;Programmer → Game Developer&quot;"></a></h3>
<ol>
<li><strong>Start:</strong> Choose engine (Unity C# or Unreal C++)</li>
<li><strong>Foundation:</strong> Intermediate-level engine course</li>
<li><strong>Deep Dive:</strong> Advanced gameplay systems</li>
<li><strong>Portfolio:</strong> Build a substantial game project</li>
</ol>
<p><strong>Estimated Cost:</strong> $40–100<br>
<strong>Timeframe:</strong> 3–6 months (if already programming)</p>
<hr>
<h3 id="hobbyist-on-budget" tabindex="-1">Hobbyist on Budget <a class="header-anchor" href="#hobbyist-on-budget" aria-label="Permalink to &quot;Hobbyist on Budget&quot;"></a></h3>
<ol>
<li><strong>Start:</strong> Unity Learn or CS50 Game Dev (Free)</li>
<li><strong>Next Step:</strong> Wait for Udemy sale, buy one focused course ($15)</li>
<li><strong>Practice:</strong> Build small games, join game jams</li>
<li><strong>Community:</strong> Join Discord servers for feedback</li>
</ol>
<p><strong>Estimated Cost:</strong> $0–30<br>
<strong>Timeframe:</strong> Learn at your own pace</p>
<hr>
<h2 id="price-comparison-summary" tabindex="-1">Price Comparison Summary <a class="header-anchor" href="#price-comparison-summary" aria-label="Permalink to &quot;Price Comparison Summary&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Platform</th>
<th>Model</th>
<th>Typical Cost</th>
<th>Value Rating</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Unity Learn</strong></td>
<td>Free</td>
<td>$0</td>
<td>⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td><strong>CS50 Games</strong></td>
<td>Free (archived)</td>
<td>$0</td>
<td>⭐⭐⭐⭐</td>
</tr>
<tr>
<td><strong>Udemy</strong></td>
<td>One-time (sales)</td>
<td>$12–30</td>
<td>⭐⭐⭐⭐⭐</td>
</tr>
<tr>
<td><strong>GDQuest</strong></td>
<td>One-time</td>
<td>$84–216</td>
<td>⭐⭐⭐⭐</td>
</tr>
<tr>
<td><strong>Skillshare</strong></td>
<td>Subscription</td>
<td>$168/year</td>
<td>⭐⭐⭐</td>
</tr>
<tr>
<td><strong>Pluralsight</strong></td>
<td>Subscription</td>
<td>$349/year</td>
<td>⭐⭐⭐</td>
</tr>
<tr>
<td><strong>Coursera</strong></td>
<td>Subscription + courses</td>
<td>$49/month</td>
<td>⭐⭐⭐⭐</td>
</tr>
<tr>
<td><strong>LinkedIn Learning</strong></td>
<td>Subscription</td>
<td>$360/year</td>
<td>⭐⭐⭐</td>
</tr>
<tr>
<td><strong>Berklee Online</strong></td>
<td>Per course</td>
<td>$1,500+</td>
<td>⭐⭐⭐⭐ (professional)</td>
</tr>
<tr>
<td><strong>University Degrees</strong></td>
<td>Degree programs</td>
<td>$20,000+</td>
<td>Career-dependent</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="tips-for-choosing-courses" tabindex="-1">Tips for Choosing Courses <a class="header-anchor" href="#tips-for-choosing-courses" aria-label="Permalink to &quot;Tips for Choosing Courses&quot;"></a></h2>
<h3 id="_1-check-the-last-updated-date" tabindex="-1">1. Check the &quot;Last Updated&quot; Date <a class="header-anchor" href="#_1-check-the-last-updated-date" aria-label="Permalink to &quot;1. Check the &quot;Last Updated&quot; Date&quot;"></a></h3>
<p>Game engines evolve rapidly. A Unity 2020 course in 2026 will have significant differences from current versions. Look for courses updated within the last 12 months.</p>
<h3 id="_2-watch-free-preview-content" tabindex="-1">2. Watch Free Preview Content <a class="header-anchor" href="#_2-watch-free-preview-content" aria-label="Permalink to &quot;2. Watch Free Preview Content&quot;"></a></h3>
<p>Most platforms offer free previews. Evaluate:</p>
<ul>
<li>Teaching style and pace</li>
<li>Audio/video quality</li>
<li>Whether explanations make sense to you</li>
</ul>
<h3 id="_3-read-recent-reviews" tabindex="-1">3. Read Recent Reviews <a class="header-anchor" href="#_3-read-recent-reviews" aria-label="Permalink to &quot;3. Read Recent Reviews&quot;"></a></h3>
<p>Sort reviews by most recent. Old 5-star reviews don't reflect current content quality.</p>
<h3 id="_4-verify-project-quality" tabindex="-1">4. Verify Project Quality <a class="header-anchor" href="#_4-verify-project-quality" aria-label="Permalink to &quot;4. Verify Project Quality&quot;"></a></h3>
<p>Look at what students build. Are the projects impressive? Would they work in a portfolio?</p>
<h3 id="_5-consider-community" tabindex="-1">5. Consider Community <a class="header-anchor" href="#_5-consider-community" aria-label="Permalink to &quot;5. Consider Community&quot;"></a></h3>
<p>Courses with active Q&amp;A sections, Discord servers, or forums provide better support when you get stuck.</p>
<h3 id="_6-match-your-learning-style" tabindex="-1">6. Match Your Learning Style <a class="header-anchor" href="#_6-match-your-learning-style" aria-label="Permalink to &quot;6. Match Your Learning Style&quot;"></a></h3>
<ul>
<li><strong>Visual learner:</strong> Video courses (Udemy, YouTube)</li>
<li><strong>Reader:</strong> Unity documentation, written tutorials</li>
<li><strong>Hands-on:</strong> Project-based courses, game jams</li>
<li><strong>Structured:</strong> University courses, specializations</li>
</ul>
<hr>
<h2 id="summary-table" tabindex="-1">Summary Table <a class="header-anchor" href="#summary-table" aria-label="Permalink to &quot;Summary Table&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Course</th>
<th>Platform</th>
<th>Price</th>
<th>Best For</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
<tr>
<td>GameDev.tv Unity 3D</td>
<td>Udemy</td>
<td>$12–30</td>
<td>Unity beginners</td>
<td>⭐ 4.7</td>
</tr>
<tr>
<td>Michigan State Specialization</td>
<td>Coursera</td>
<td>$49/mo</td>
<td>Formal credentials</td>
<td>⭐ 4.7</td>
</tr>
<tr>
<td>Unity Learn Pathways</td>
<td>Unity</td>
<td>Free</td>
<td>Budget learners</td>
<td>⭐ 4.5</td>
</tr>
<tr>
<td>Stephen Ulibarri UE5</td>
<td>Udemy</td>
<td>$15–30</td>
<td>Unreal C++</td>
<td>⭐ 4.8</td>
</tr>
<tr>
<td>Coursera UE Specialization</td>
<td>Coursera</td>
<td>$199</td>
<td>UE beginners</td>
<td>⭐ 4.6</td>
</tr>
<tr>
<td>GDQuest Bundles</td>
<td>GDQuest</td>
<td>$84–216</td>
<td>Godot learners</td>
<td>⭐ 4.8</td>
</tr>
<tr>
<td>Godot Beginner Course</td>
<td>Udemy</td>
<td>$15–25</td>
<td>Budget Godot</td>
<td>⭐ 4.5</td>
</tr>
<tr>
<td>Harvard CS50 Games</td>
<td>edX (archived)</td>
<td>Free</td>
<td>Fundamentals</td>
<td>⭐ 4.6</td>
</tr>
<tr>
<td>Complete Blender Creator</td>
<td>Udemy</td>
<td>$15–30</td>
<td>3D modeling</td>
<td>⭐ 4.7</td>
</tr>
<tr>
<td>Berklee Game Audio</td>
<td>Berklee Online</td>
<td>$4,500</td>
<td>Pro audio</td>
<td>⭐ 4.8</td>
</tr>
<tr>
<td>Game Audio Essentials</td>
<td>Udemy</td>
<td>$15–25</td>
<td>Budget audio</td>
<td>⭐ 4.4</td>
</tr>
</tbody>
</table>
<hr>
<p>Ready to start learning? Pick a course that matches your budget, skill level, and preferred engine. The most important step is to <strong>start building games</strong> — courses are just tools to help you get there faster.</p>
<p>For more resources, check out:</p>
<ul>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a></li>
<li><a href="/guides/game-jams-hackathons.html">Game Jams &amp; Hackathons Guide</a> — the best way to learn is to ship under pressure</li>
<li><a href="/guides/itch-io-launch-guide.html">How to Launch Your Game on itch.io</a></li>
<li><a href="/guides/web-game-engines-comparison.html">Web Game Engines Comparison</a> — picking the right engine before choosing a course</li>
<li><a href="/guides/game-asset-licenses.html">Game Asset Licenses Explained</a> — understanding licenses before using assets from courses</li>
<li><a href="/tutorials/canvas-2d-game-loop.html">Canvas 2D game loop</a> — start building without a course, free tutorial</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Game Jams & Hackathons Guide]]></title>
            <link>https://app.cinevva.com/guides/game-jams-hackathons</link>
            <guid>https://app.cinevva.com/guides/game-jams-hackathons</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Complete guide to game jams and hackathons - what they are, major events, why web games excel, and how Cinevva helps you launch your jam game]]></description>
            <content:encoded><![CDATA[<h1 id="game-jams-hackathons-guide" tabindex="-1">Game Jams &amp; Hackathons Guide <a class="header-anchor" href="#game-jams-hackathons-guide" aria-label="Permalink to &quot;Game Jams &amp; Hackathons Guide&quot;"></a></h1>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:8px;margin:1.5rem 0">
<iframe src="https://www.youtube.com/embed/btOlp4_d1tc" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe>
</div>
<p style="font-size:0.9rem;color:var(--vp-c-text-2);margin-top:-0.5rem">5 tips to win game jams like GMTK and Ludum Dare</p>
<p>Game jams are where indie games are born. You create a game in 48-72 hours, get immediate feedback from thousands of players, and sometimes launch something that becomes a real hit. This guide covers what you need to know.</p>
<h2 id="what-s-a-game-jam" tabindex="-1">What's a Game Jam? <a class="header-anchor" href="#what-s-a-game-jam" aria-label="Permalink to &quot;What's a Game Jam?&quot;"></a></h2>
<p>A game jam is a time-limited event where you create a game from scratch, usually around a theme announced at the start. Think of it as a hackathon for game developers. They typically last 48 hours to 30 days, with teams of one to four people, and the goal is a playable prototype.</p>
<p>Game jams aren't about creating polished, commercial products. They're about experimentation, creative problem-solving under constraints, and building something fun in a short time.</p>
<h2 id="why-they-matter" tabindex="-1">Why They Matter <a class="header-anchor" href="#why-they-matter" aria-label="Permalink to &quot;Why They Matter&quot;"></a></h2>
<p>For new developers, nothing beats shipping a complete game, even a small one. Imperfection is expected, so it's safe to experiment. Other participants play and rate your game, giving you instant feedback. And a finished jam game shows you can execute, not just talk.</p>
<p>For experienced developers, jams let you test ideas fast and validate concepts before committing months. Constraints spark creativity and break creative blocks. You meet collaborators, publishers, and players. And many successful indie games started as jam entries.</p>
<p>Celeste began as a Ludum Dare game. Superhot started as a 7-day prototype. Hollow Knight evolved from a jam concept. Minit originated from a 48-hour jam. Papers, Please was built for Ludum Dare 29.</p>
<h2 id="major-game-jams-in-2026" tabindex="-1">Major Game Jams in 2026 <a class="header-anchor" href="#major-game-jams-in-2026" aria-label="Permalink to &quot;Major Game Jams in 2026&quot;"></a></h2>
<h3 id="global-events" tabindex="-1">Global Events <a class="header-anchor" href="#global-events" aria-label="Permalink to &quot;Global Events&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Event</th>
<th>When</th>
<th>Format</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Global Game Jam</strong></td>
<td>Late January</td>
<td>48 hours, local &amp; remote sites</td>
<td>Largest jam, 40,000+ participants worldwide. The 2026 edition (Jan 26-Feb 1) is where GGJ crosses 500,000 lifetime participants since it started, across roughly 800 sites in more than 100 countries.</td>
</tr>
<tr>
<td><strong>Ludum Dare</strong></td>
<td>April, October (approx.)</td>
<td>48h solo / 72h team</td>
<td>One of the oldest and most respected jams</td>
</tr>
<tr>
<td><strong>GMTK Game Jam</strong></td>
<td>Summer</td>
<td>48 hours</td>
<td>Huge viewership, 7,000+ entries typical</td>
</tr>
<tr>
<td><strong>js13kGames</strong></td>
<td>August-September</td>
<td>13KB limit, 30 days</td>
<td>Best for web developers</td>
</tr>
<tr>
<td><strong>Gamedev.js Jam</strong></td>
<td>April 13-26, 2026</td>
<td>13 days, HTML5 only</td>
<td>Prizes, accessible format</td>
</tr>
</tbody>
</table>
<h3 id="ongoing-regular-jams-on-itch-io" tabindex="-1">Ongoing &amp; Regular Jams on itch.io <a class="header-anchor" href="#ongoing-regular-jams-on-itch-io" aria-label="Permalink to &quot;Ongoing &amp; Regular Jams on itch.io&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Jam</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Brackeys Game Jam 2026.2</strong></td>
<td>7 days, thousands of participants, Aug 23-30, 2026</td>
</tr>
<tr>
<td><strong>Pirate Software Game Jam 18</strong></td>
<td>14 days, ~600+ participants</td>
</tr>
<tr>
<td><strong>Finally Finish Something 2026</strong></td>
<td>January, for completing WIPs</td>
</tr>
<tr>
<td><strong>2026 Annual Jam</strong></td>
<td>Year-long, relaxed rules</td>
</tr>
<tr>
<td><strong>Retro Game Jam 2026</strong></td>
<td>Feb 8-14, retro aesthetic focus</td>
</tr>
</tbody>
</table>
<p><strong>Find more:</strong> <a href="https://itch.io/jams" target="_blank" rel="noreferrer">itch.io/jams</a> lists hundreds of active and upcoming jams.</p>
<hr>
<h2 id="why-web-games-win-jams" tabindex="-1">Why Web Games Win Jams <a class="header-anchor" href="#why-web-games-win-jams" aria-label="Permalink to &quot;Why Web Games Win Jams&quot;"></a></h2>
<p>If you can make a browser-playable game, you have a massive advantage.</p>
<h3 id="zero-friction" tabindex="-1">Zero Friction <a class="header-anchor" href="#zero-friction" aria-label="Permalink to &quot;Zero Friction&quot;"></a></h3>
<p>Jam judges play dozens of games. With a downloadable game, they click download, wait for it, extract or install, find the executable, and launch. That's 5-10 minutes before they even start playing. With a web game, they click play and wait 2-5 seconds. They're already playing.</p>
<p>Every second of friction costs you plays, ratings, and rankings.</p>
<h3 id="works-everywhere" tabindex="-1">Works Everywhere <a class="header-anchor" href="#works-everywhere" aria-label="Permalink to &quot;Works Everywhere&quot;"></a></h3>
<p>Web games run on Windows, Mac, Linux, and ChromeOS. They work on mobile if you support touch. You never have to write &quot;Sorry, Windows only&quot; in your description. You maximize your potential audience.</p>
<h3 id="easy-to-share" tabindex="-1">Easy to Share <a class="header-anchor" href="#easy-to-share" aria-label="Permalink to &quot;Easy to Share&quot;"></a></h3>
<p>You get a direct link to play. You can embed in tweets, Discord, and blogs. No sketchy &quot;Download from Google Drive&quot; links. Better for social media virality.</p>
<h3 id="instant-updates" tabindex="-1">Instant Updates <a class="header-anchor" href="#instant-updates" aria-label="Permalink to &quot;Instant Updates&quot;"></a></h3>
<p>Bug fix? Deploy in seconds. Found a balance issue? Patch it live. No &quot;v1.1 uploaded, please re-download&quot; messages.</p>
<h3 id="technical-capabilities" tabindex="-1">Technical Capabilities <a class="header-anchor" href="#technical-capabilities" aria-label="Permalink to &quot;Technical Capabilities&quot;"></a></h3>
<p>WebGL and WebGPU give you GPU-accelerated graphics. Web Audio API provides full sound support. LocalStorage and IndexedDB let you save games without a server. Service Workers make offline play possible. WebSockets enable multiplayer.</p>
<h2 id="getting-your-jam-game-discovered" tabindex="-1">Getting Your Jam Game Discovered <a class="header-anchor" href="#getting-your-jam-game-discovered" aria-label="Permalink to &quot;Getting Your Jam Game Discovered&quot;"></a></h2>
<p>Game jams generate thousands of games. After a jam, your game competes with thousands of other entries on itch.io, an overwhelming &quot;New&quot; feed, and players with limited time who skim.</p>
<p>Traditional solutions don't work well. Screenshots don't show gameplay. Trailers take hours to make and you're exhausted. Marketing requires energy you don't have.</p>
<h3 id="short-form-gameplay-works" tabindex="-1">Short-Form Gameplay Works <a class="header-anchor" href="#short-form-gameplay-works" aria-label="Permalink to &quot;Short-Form Gameplay Works&quot;"></a></h3>
<p>Record 15 seconds of gameplay showing the best moment, the core loop, the hook. Upload to Cinevva and link to your itch.io page. Players scroll, watch, click, and play instantly.</p>
<p>Your game gets judged by its gameplay, not your marketing skills. You don't need time for trailers because 15-second clips are enough. You don't need to compete with AAA marketing because gameplay speaks for itself. And players don't need to download because web games play instantly.</p>
<h3 id="the-workflow" tabindex="-1">The Workflow <a class="header-anchor" href="#the-workflow" aria-label="Permalink to &quot;The Workflow&quot;"></a></h3>
<p>During the jam, focus on making your game. In the last hour, record 2-3 quick gameplay clips.</p>
<p>After submission, submit to the jam normally, upload your clips to Cinevva, share the link on social media, and watch real players discover and play your game.</p>
<h2 id="success-strategies" tabindex="-1">Success Strategies <a class="header-anchor" href="#success-strategies" aria-label="Permalink to &quot;Success Strategies&quot;"></a></h2>
<h3 id="before-the-jam" tabindex="-1">Before the Jam <a class="header-anchor" href="#before-the-jam" aria-label="Permalink to &quot;Before the Jam&quot;"></a></h3>
<p>Prepare your tools. Test your engine, IDE, and art tools. Set up version control with Git. Have a project template ready. Practice importing assets and building basic mechanics.</p>
<p>Prepare yourself. Sleep well the night before. Stock up on food and drinks. Clear your schedule. Tell family or roommates you're going to be busy.</p>
<p>If you have a team, agree on communication (Discord works well), assign rough roles, and set expectations for availability.</p>
<h3 id="during-the-jam" tabindex="-1">During the Jam <a class="header-anchor" href="#during-the-jam" aria-label="Permalink to &quot;During the Jam&quot;"></a></h3>
<p>In the first 2 hours, brainstorm and plan. Generate multiple ideas and pick the most achievable. Scope ruthlessly and cut by 50%. Document your core loop. Agree on visual style.</p>
<p>From hours 2 to 20, build the core. Get the main mechanic working first. Placeholder art is fine. No polish until the core works. Commit frequently.</p>
<p>From hours 20 to 40, make it playable. Add content like levels and enemies. Build basic UI with menu and game over screens. Add sound effects and music. Keep testing constantly.</p>
<p>In the final 8 hours, polish and ship. Fix critical bugs only. Do a final art pass. Write your description. Capture screenshots and a GIF. Submit early because you don't want to miss the deadline.</p>
<h3 id="after-the-jam" tabindex="-1">After the Jam <a class="header-anchor" href="#after-the-jam" aria-label="Permalink to &quot;After the Jam&quot;"></a></h3>
<p>Immediately, rest because you earned it. Share on social media. Thank your teammates.</p>
<p>Within a week, play and rate other entries, respond to comments, collect feedback, and record gameplay clips.</p>
<p>Later, decide if you want to continue development, write a post-mortem devlog, and connect with people you met.</p>
<h3 id="common-mistakes" tabindex="-1">Common Mistakes <a class="header-anchor" href="#common-mistakes" aria-label="Permalink to &quot;Common Mistakes&quot;"></a></h3>
<p>Scope creep happens from excitement. Write your features list early and stick to it. No sound happens when you leave it for last. Add placeholder sounds early. Unclear controls come from developer blindness. Include instructions and test with others. Broken builds come from rushing. Test exports early in the jam. Bad descriptions come from exhaustion. Draft yours before you're too tired.</p>
<h2 id="making-web-games-for-jams" tabindex="-1">Making Web Games for Jams <a class="header-anchor" href="#making-web-games-for-jams" aria-label="Permalink to &quot;Making Web Games for Jams&quot;"></a></h2>
<p>For 2D games, Phaser is the most popular HTML5 game framework. Pixi.js is a fast 2D renderer you can pair with your own logic. Godot has excellent HTML5 export.</p>
<p>For 3D games, Three.js is the most flexible WebGL library. Babylon.js is a full-featured 3D engine. PlayCanvas is web-native with a built-in editor.</p>
<p>For no-code or low-code, GDevelop is a visual game maker with web export. Construct makes powerful no-code HTML5 games.</p>
<h3 id="optimization" tabindex="-1">Optimization <a class="header-anchor" href="#optimization" aria-label="Permalink to &quot;Optimization&quot;"></a></h3>
<p>Keep loading time under 10 seconds by compressing assets and lazy loading. Show loading progress so players don't abandon. Test on Chrome, Firefox, and Safari because browser quirks exist. Make it mobile-friendly if possible with touch controls and responsive layout. Provide a fullscreen button for immersion. Include a sound toggle since not everyone can have sound on.</p>
<p>For itch.io embed settings, use 960px width (or your native resolution), 540px height for 16:9, enable mobile-friendly if responsive, enable the fullscreen button, and disable scrollbars.</p>
<h2 id="after-the-jam-1" tabindex="-1">After the Jam <a class="header-anchor" href="#after-the-jam-1" aria-label="Permalink to &quot;After the Jam&quot;"></a></h2>
<p>In the first week, respond to all feedback to build goodwill and learn. Play and rate other entries because community matters. Post a devlog sharing your experience and what worked or didn't. Upload gameplay clips to get more eyes on your game.</p>
<p>In the first month, fix bugs players reported, make minor polish improvements that are low-effort but high-impact, and share your jam story on social media with GIFs.</p>
<p>Long-term, if the game has potential, collect feedback themes to see what players consistently liked or disliked. Assess whether there's a larger game here. Plan a post-jam version that's expanded and polished. Consider monetization through PWYW on itch.io or a Steam release.</p>
<p>Signs your jam game could be more: players asking for more content, streamers picking it up, high ratings relative to similar games, you're still excited about it weeks later, and there's a clear path to 10x the content.</p>
<h2 id="resources" tabindex="-1">Resources <a class="header-anchor" href="#resources" aria-label="Permalink to &quot;Resources&quot;"></a></h2>
<p>For communities, check <a href="https://reddit.com/r/gamejams" target="_blank" rel="noreferrer">r/gamejams</a> for active jam discussion, <a href="https://reddit.com/r/ludumdare" target="_blank" rel="noreferrer">r/ludumdare</a> for LD-specific talk, <a href="https://itch.io/jams" target="_blank" rel="noreferrer">itch.io Jams</a> to find and join jams, and <a href="https://gamedevjs.com" target="_blank" rel="noreferrer">Gamedev.js</a> for the JavaScript game dev community.</p>
<p>For tools, <a href="https://kenney.nl" target="_blank" rel="noreferrer">Kenney Assets</a> has free game assets, <a href="https://opengameart.org" target="_blank" rel="noreferrer">OpenGameArt</a> has CC-licensed art and audio, <a href="https://freesound.org" target="_blank" rel="noreferrer">Freesound</a> has sound effects, and <a href="https://itch.io/docs/butler/" target="_blank" rel="noreferrer">itch.io Butler</a> is a CLI for fast uploads.</p>
<p>For learning, check <a href="/tutorials/">Cinevva Tutorials</a> for web game development guides, <a href="/guides/web-game-engines-comparison.html">Web Game Engines Comparison</a> to choose your tools, and <a href="/guides/itch-io-launch-guide.html">How to Launch on itch.io</a> to optimize your game page.</p>
<h2 id="your-next-jam" tabindex="-1">Your Next Jam <a class="header-anchor" href="#your-next-jam" aria-label="Permalink to &quot;Your Next Jam&quot;"></a></h2>
<p>Before the jam, install and test your tools, prepare a template project, gather asset packs if allowed, assemble your team or decide to go solo, and block your calendar.</p>
<p>During the jam, brainstorm on theme for 30 minutes max, prototype the core mechanic first, test early and often, add sound before the final day, and leave at least an hour for submission.</p>
<p>After the jam, submit to the jam page, upload gameplay clips, play and rate other entries, respond to feedback, and write a devlog about your experience.</p>
<h2 id="get-started" tabindex="-1">Get Started <a class="header-anchor" href="#get-started" aria-label="Permalink to &quot;Get Started&quot;"></a></h2>
<p>Browse upcoming jams at <a href="https://itch.io/jams" target="_blank" rel="noreferrer">itch.io/jams</a>. Pick one that fits your schedule and interests. Prepare your tools using this guide. Make something awesome in 48-72 hours. Share it and watch players discover it.</p>
<p>Game jams are where indie games are born. Your next jam game could be the start of something big.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> — CC0 asset packs ready for jam games</li>
<li><a href="/tutorials/agentic-code-tools.html">Agentic AI code tools</a> — using Claude Code, Cursor, and Copilot to build faster during jams</li>
<li><a href="/guides/kickstarter-for-indie-games.html">Kickstarter for Indie Games</a> — turning a successful jam game into a funded project</li>
<li><a href="/guides/co-op-game-design.html">Co-op Game Design</a> — co-op jam games get more attention and ratings</li>
<li><a href="/tutorials/canvas-2d-game-loop.html">Canvas 2D game loop</a> — the simplest foundation for a 48-hour jam game</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Launch Your Game on itch.io (2026 Guide)]]></title>
            <link>https://app.cinevva.com/guides/itch-io-launch-guide</link>
            <guid>https://app.cinevva.com/guides/itch-io-launch-guide</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How to launch, market, and grow your indie game on itch.io in 2026 - page setup, pricing, tags, devlogs, and getting discovered.]]></description>
            <content:encoded><![CDATA[<h1 id="how-to-launch-your-game-on-itch-io-2026-guide" tabindex="-1">How to Launch Your Game on itch.io (2026 Guide) <a class="header-anchor" href="#how-to-launch-your-game-on-itch-io-2026-guide" aria-label="Permalink to &quot;How to Launch Your Game on itch.io (2026 Guide)&quot;"></a></h1>
<p><em>Last updated: June 2026.</em></p>
<p>This guide covers everything I've learned about launching games on itch.io. From page setup to pricing to visibility, these are the strategies that actually work.</p>
<h2 id="why-itch-io" tabindex="-1">Why itch.io? <a class="header-anchor" href="#why-itch-io" aria-label="Permalink to &quot;Why itch.io?&quot;"></a></h2>
<p>itch.io is where indie games thrive. There's almost no gatekeeping, so anyone can publish, with one caveat worth knowing up front: since mid-2025 itch.io has tightened how adult/NSFW content is handled. Under pressure from its payment processors, it deindexed adult content in July 2025 and now requires NSFW pages to add content warnings to be searchable, with paid adult content still restricted. For the vast majority of non-adult games, none of this applies and publishing is as open as ever. Pricing is flexible: free, fixed, or pay-what-you-want. You set your own revenue share, even 0% if you want to keep everything. Web games run natively in the browser. The community actively seeks out indie games. And the game jam ecosystem gives you built-in visibility and feedback.</p>
<p>It's not Steam, and that's the point. itch.io rewards experimentation and rewards developers who engage with the community.</p>
<h2 id="before-you-publish" tabindex="-1">Before You Publish <a class="header-anchor" href="#before-you-publish" aria-label="Permalink to &quot;Before You Publish&quot;"></a></h2>
<p>You need a few things ready before you hit publish. At minimum, you need a playable game (downloadable or browser-playable), a cover image at 630×500 pixels, at least 3 screenshots, a description, and genre/tags selected.</p>
<p>For a better launch, add a trailer or gameplay GIF, a detailed description with features, a press kit, social media accounts, and optionally a Discord server.</p>
<p>For the best results, plan your devlog posts in advance, prepare an influencer and press outreach list, draft your launch announcement, and build community before you launch.</p>
<h2 id="your-page-is-your-storefront" tabindex="-1">Your Page Is Your Storefront <a class="header-anchor" href="#your-page-is-your-storefront" aria-label="Permalink to &quot;Your Page Is Your Storefront&quot;"></a></h2>
<p>The page is where people decide whether to play your game. Every element matters.</p>
<h3 id="cover-image" tabindex="-1">Cover Image <a class="header-anchor" href="#cover-image" aria-label="Permalink to &quot;Cover Image&quot;"></a></h3>
<p>Your cover image is the most important visual. It shows up in browse pages, search results, collections, and social media embeds. At 630×500 pixels (or 315×250 minimum), it needs to work at thumbnail size.</p>
<p>Make sure it's legible when small. Test it at thumbnail scale. Show your game's vibe through art style, mood, and genre cues. Include the title and make it readable. Use bold colors and a clear focal point to stand out in a grid of other games.</p>
<h3 id="screenshots-and-media" tabindex="-1">Screenshots and Media <a class="header-anchor" href="#screenshots-and-media" aria-label="Permalink to &quot;Screenshots and Media&quot;"></a></h3>
<p>You need 3-5 screenshots at minimum for credibility. A GIF showing gameplay in motion helps a lot. For serious launches, add a 30-90 second trailer.</p>
<p>For screenshots, show actual gameplay, not menus. Capture exciting moments. Keep a consistent aspect ratio. Include UI if it's attractive.</p>
<p>For GIFs, aim for 5-15 seconds of action. Keep them under 10MB so they load fast. Show the core gameplay loop. Make them loop smoothly.</p>
<h3 id="description" tabindex="-1">Description <a class="header-anchor" href="#description" aria-label="Permalink to &quot;Description&quot;"></a></h3>
<p>Structure your description for people who skim. Lead with a catchy one-liner that hooks them. Follow with a sentence explaining the genre, what you do, and the goal. Add 2-3 sentences expanding on the concept. Then list features, controls, and credits.</p>
<p>Don't write walls of text. Don't start with &quot;This is my first game...&quot; or be overly humble. And don't forget to explain what the game actually is. People need to understand what they're getting into.</p>
<h3 id="tags" tabindex="-1">Tags <a class="header-anchor" href="#tags" aria-label="Permalink to &quot;Tags&quot;"></a></h3>
<p>Tags determine where your game appears in Browse. Use all relevant tags. Be specific: not just &quot;action&quot; but &quot;top-down-shooter.&quot; Include accessibility tags if applicable. Add multiplayer or local-coop tags if relevant. Use popular tags your game genuinely fits.</p>
<p>High-value tags to consider: specific genres like roguelike, metroidvania, or visual-novel. Art styles like pixel-art, low-poly, or hand-drawn. Themes like horror, cozy, or atmospheric. Multiplayer modes like local-multiplayer or online-co-op. Accessibility features like colorblind-friendly or one-button.</p>
<h3 id="metadata" tabindex="-1">Metadata <a class="header-anchor" href="#metadata" aria-label="Permalink to &quot;Metadata&quot;"></a></h3>
<p>Fill in everything. Genre, engine/tools used, as many relevant tags as possible, all supported languages, accessibility features like configurable controls, and multiplayer mode (single-player, local, online). The more complete your metadata, the more discoverable your game becomes.</p>
<h2 id="pricing" tabindex="-1">Pricing <a class="header-anchor" href="#pricing" aria-label="Permalink to &quot;Pricing&quot;"></a></h2>
<p>itch.io gives you three pricing models: free, pay-what-you-want, and fixed price. Each works for different situations.</p>
<p>itch.io runs an open revenue-share model: the default cut is about 10%, but you choose the percentage, anywhere from 0% to 100%. A few times a year itch.io also runs Creator Day, a 24-hour event where it waives its platform fee entirely so you keep 100% of sales (after payment-processor fees and taxes). It's worth timing a sale or launch announcement around one. The most recent ran November 28, 2025.</p>
<h3 id="pay-what-you-want-works-best-for-most-games" tabindex="-1">Pay-What-You-Want Works Best for Most Games <a class="header-anchor" href="#pay-what-you-want-works-best-for-most-games" aria-label="Permalink to &quot;Pay-What-You-Want Works Best for Most Games&quot;"></a></h3>
<p>PWYW with a $0 minimum typically outperforms fixed pricing for indie games. You'll get 3-5x more downloads than fixed price. About 10-30% of downloaders choose to pay something. Total revenue often ends up higher despite the lower per-buyer average.</p>
<p>To optimize PWYW, set a suggested price to anchor expectations. For short games under 30 minutes, suggest $2-5. For medium games of 1-3 hours, suggest $5-10. For full games over 5 hours, suggest $10-20.</p>
<p>Explain why support helps. Something like &quot;This game is pay-what-you-want! Any support helps me make more games and keeps updates coming.&quot; And offer incentives for paying: bonus content, soundtrack, behind-the-scenes PDF, or name in credits.</p>
<h3 id="when-fixed-pricing-makes-sense" tabindex="-1">When Fixed Pricing Makes Sense <a class="header-anchor" href="#when-fixed-pricing-makes-sense" aria-label="Permalink to &quot;When Fixed Pricing Makes Sense&quot;"></a></h3>
<p>Use fixed pricing when you have an established audience, when the game is highly polished, when there's significant content (5+ hours), or when you need predictable revenue.</p>
<p>For price points, $2-5 works for short games and prototypes. $5-10 for medium games with niche appeal. $10-20 for full games with high polish. $20+ for large games from established developers.</p>
<h3 id="free-games-still-make-money" tabindex="-1">Free Games Still Make Money <a class="header-anchor" href="#free-games-still-make-money" aria-label="Permalink to &quot;Free Games Still Make Money&quot;"></a></h3>
<p>For portfolio pieces, game jam entries, experimental projects, or building audience for future paid games, free makes sense. But even free games get donations. About 30% of itch.io spending comes from amounts paid above the minimum.</p>
<h2 id="getting-discovered" tabindex="-1">Getting Discovered <a class="header-anchor" href="#getting-discovered" aria-label="Permalink to &quot;Getting Discovered&quot;"></a></h2>
<p>itch.io doesn't have Steam's algorithmic promotion. You drive your own visibility.</p>
<p>Discovery works through search (driven by tags, title, description), Browse Popular (driven by downloads, ratings, recency), Browse New (driven by publish date), collections (players adding your game), devlogs (your updates), and occasional editorial features (staff picks, mostly luck).</p>
<h3 id="getting-indexed" tabindex="-1">Getting Indexed <a class="header-anchor" href="#getting-indexed" aria-label="Permalink to &quot;Getting Indexed&quot;"></a></h3>
<p>Your game must be indexed to appear in search and browse. You need to be published publicly (not unlisted), have playable or downloadable content, have a cover image, not be a placeholder page, and follow content guidelines.</p>
<p>Things that prevent indexing: external-link-only pages, no downloads or playable content, unlisted visibility setting, and missing cover images. If your game is tagged adult/NSFW, there's an extra step: since 2025 itch.io requires those pages to add a content warning to be indexed, and paid adult content is currently kept out of search while itch.io works with payment processors. Non-adult games aren't affected.</p>
<h3 id="the-most-recent-boost" tabindex="-1">The &quot;Most Recent&quot; Boost <a class="header-anchor" href="#the-most-recent-boost" aria-label="Permalink to &quot;The &quot;Most Recent&quot; Boost&quot;"></a></h3>
<p>When you first publish, you appear in &quot;Most Recent&quot; which gives a small visibility boost. This only happens once, so timing matters.</p>
<p>Publish when your page is complete. Don't publish early then update because you only get one &quot;new&quot; boost. Avoid weekends when more games release. Consider the timezone of your target audience.</p>
<h3 id="devlogs-are-your-secret-weapon" tabindex="-1">Devlogs Are Your Secret Weapon <a class="header-anchor" href="#devlogs-are-your-secret-weapon" aria-label="Permalink to &quot;Devlogs Are Your Secret Weapon&quot;"></a></h3>
<p>Devlogs show in followers' feeds, appear in &quot;Recent Devlogs&quot; sections, signal active development, build community trust, and help with SEO.</p>
<p>Post during development to build anticipation. Post at launch for the announcement. Post after launch for updates and new features. Aim for 1-2 per month during active development.</p>
<p>Good topics include behind-the-scenes process, new features and content, responses to player feedback, technical challenges you solved, art and music showcases, and sales milestones (thank your players).</p>
<h2 id="game-jams" tabindex="-1">Game Jams <a class="header-anchor" href="#game-jams" aria-label="Permalink to &quot;Game Jams&quot;"></a></h2>
<p>Game jams are the single best way to get visibility on itch.io. You get a built-in audience with thousands of views in days, a dedicated gallery where your game is browseable, feedback through ratings and comments, networking with other developers, portfolio pieces that show your range, and low stakes to prototype freely.</p>
<h3 id="jam-strategy" tabindex="-1">Jam Strategy <a class="header-anchor" href="#jam-strategy" aria-label="Permalink to &quot;Jam Strategy&quot;"></a></h3>
<p>For choosing jams, large ones like Ludum Dare and GMTK bring more traffic but more competition. Small or niche jams give you a better chance to stand out. Theme-aligned jams let you play to your strengths.</p>
<p>During the jam, post progress on social media, use jam hashtags, document your process, and engage with other participants.</p>
<p>For your jam page, polish it even for a jam game. Good screenshots matter. Write a clear description of what the game is. Include controls.</p>
<p>After the jam, play and rate other entries for networking. Respond to all feedback. Consider post-jam updates. Write a devlog about what you learned.</p>
<h3 id="jam-games-can-become-real-games" tabindex="-1">Jam Games Can Become Real Games <a class="header-anchor" href="#jam-games-can-become-real-games" aria-label="Permalink to &quot;Jam Games Can Become Real Games&quot;"></a></h3>
<p>Many successful indie games started as jam entries. Celeste started as a jam game. Superhot began as a 7-day prototype. Hollow Knight evolved from a jam concept.</p>
<p>The path is: collect feedback, identify what worked, polish and expand, then re-release as standalone.</p>
<h2 id="marketing-without-money" tabindex="-1">Marketing Without Money <a class="header-anchor" href="#marketing-without-money" aria-label="Permalink to &quot;Marketing Without Money&quot;"></a></h2>
<p>You don't need a budget to market on itch.io. You need consistency.</p>
<h3 id="social-media" tabindex="-1">Social Media <a class="header-anchor" href="#social-media" aria-label="Permalink to &quot;Social Media&quot;"></a></h3>
<p>Be on Twitter/X for the largest gamedev community. Use Reddit, specifically r/indiegaming, r/playmygame, and genre-specific subreddits. Try TikTok for short gameplay clips. Build a Discord server and join relevant communities. Post devlogs and trailers on YouTube.</p>
<p>For a content calendar, post weekly for Screenshot Saturday and progress updates. Post bi-weekly devlogs. Announce bigger milestones monthly.</p>
<p>Post GIFs of gameplay, behind-the-scenes process, development challenges, before/after comparisons, memes about your game, and player reactions.</p>
<h3 id="reaching-out-to-influencers" tabindex="-1">Reaching Out to Influencers <a class="header-anchor" href="#reaching-out-to-influencers" aria-label="Permalink to &quot;Reaching Out to Influencers&quot;"></a></h3>
<p>Micro-influencers with 1K-50K followers are your friends. Find them by searching &quot;[your genre] indie games&quot; on YouTube, looking for streamers playing similar games, and checking who covered games like yours.</p>
<p>When you reach out, personalize each email. Show you know their content. Make it easy by including all the links. Be brief. Don't mass email, don't demand coverage, don't be pushy about timing. Follow up once, politely.</p>
<p>A simple template works: introduce yourself, mention you noticed they cover your genre, describe your game in one sentence, explain what makes it unique, offer a free key with no pressure, and include links to your page and press kit.</p>
<h3 id="press" tabindex="-1">Press <a class="header-anchor" href="#press" aria-label="Permalink to &quot;Press&quot;"></a></h3>
<p>For bigger launches, contact indie game press like IndieGamesPlus, Rock Paper Shotgun, Kotaku's indie section, and genre-specific sites. Same principles as influencer outreach: personalize, be brief, make their job easy.</p>
<h2 id="launch-timeline" tabindex="-1">Launch Timeline <a class="header-anchor" href="#launch-timeline" aria-label="Permalink to &quot;Launch Timeline&quot;"></a></h2>
<p>Three to six months before launch, start your social media presence, join relevant communities, begin devlog posts, and build followers.</p>
<p>One to two months before, finalize page assets like screenshots and trailer, prepare your press kit, draft your influencer and press list, and announce your release date.</p>
<p>One to two weeks before, send keys to influencers and press, schedule your launch posts, prepare your launch devlog, and give your page a final polish.</p>
<p>On launch day, publish the game, post your launch announcement everywhere, engage with all comments, monitor for bugs, and thank early players.</p>
<p>During the first week after launch, respond to all feedback, fix critical bugs immediately, post an update devlog, and share positive reviews.</p>
<p>Ongoing after launch, keep releasing regular updates, engage with the community, plan future content, and consider sales and bundles.</p>
<h2 id="common-mistakes" tabindex="-1">Common Mistakes <a class="header-anchor" href="#common-mistakes" aria-label="Permalink to &quot;Common Mistakes&quot;"></a></h2>
<p>For page mistakes, don't publish before your page is ready. Don't skip the cover image or use a bad one. Don't write wall-of-text descriptions. Don't launch without screenshots. Don't use wrong or missing tags.</p>
<p>For marketing mistakes, don't wait until launch to start marketing. Don't just post &quot;game is out&quot; once. Engage with the community. Don't expect itch.io to do the marketing for you. Don't ignore feedback.</p>
<p>For pricing mistakes, don't set a high price without an established audience. Don't undervalue your work at $0.99 for 10+ hours of content. Always set a suggested price on PWYW. Run sales occasionally.</p>
<p>For development mistakes, don't let scope creep delay your launch forever. Don't wait for &quot;perfect&quot; instead of shipping. Collect early feedback. Don't abandon the game after launch.</p>
<h2 id="analytics" tabindex="-1">Analytics <a class="header-anchor" href="#analytics" aria-label="Permalink to &quot;Analytics&quot;"></a></h2>
<p>Track page views (how many found your page), downloads (how many tried your game), conversion rate (views to downloads), ratings count (engagement level), rating average (quality perception), revenue, and referrers (where traffic comes from).</p>
<h3 id="diagnosing-problems" tabindex="-1">Diagnosing Problems <a class="header-anchor" href="#diagnosing-problems" aria-label="Permalink to &quot;Diagnosing Problems&quot;"></a></h3>
<p>If you have low views, it's a marketing problem. Post more on social media, try new communities, use better hashtags, write more devlogs.</p>
<p>If you have high views but low downloads, it's a page problem. Improve your cover image, get better screenshots, write a clearer description, check your pricing.</p>
<p>If you have high downloads but low ratings, it's a game quality problem. Fix bugs, improve the tutorial and onboarding, address common complaints.</p>
<h2 id="long-term-growth" tabindex="-1">Long-Term Growth <a class="header-anchor" href="#long-term-growth" aria-label="Permalink to &quot;Long-Term Growth&quot;"></a></h2>
<h3 id="building-an-audience" tabindex="-1">Building an Audience <a class="header-anchor" href="#building-an-audience" aria-label="Permalink to &quot;Building an Audience&quot;"></a></h3>
<p>itch.io followers are valuable. They get notified of new releases, they see your devlogs, and they're likely to buy future games.</p>
<p>Grow followers by asking at the end of your games, mentioning it in devlogs, linking from social media, and focusing on quality over quantity.</p>
<h3 id="your-catalog-compounds" tabindex="-1">Your Catalog Compounds <a class="header-anchor" href="#your-catalog-compounds" aria-label="Permalink to &quot;Your Catalog Compounds&quot;"></a></h3>
<p>Your second game is easier to launch than your first. You have existing followers, you can cross-promote between games, you have an established reputation, and you've learned skills.</p>
<p>The strategy is: launch first game and collect followers, promote game 2 to game 1 players, bundle games together, and let each game grow your audience.</p>
<h3 id="sales-and-bundles" tabindex="-1">Sales and Bundles <a class="header-anchor" href="#sales-and-bundles" aria-label="Permalink to &quot;Sales and Bundles&quot;"></a></h3>
<p>For running sales, 20-50% off is common. Time them around holidays. Announce on social media. Update your devlog.</p>
<p>For joining bundles, they increase exposure, bring new players, and work well for older games. itch.io sometimes organizes charity bundles worth joining.</p>
<h2 id="web-games-on-itch-io" tabindex="-1">Web Games on itch.io <a class="header-anchor" href="#web-games-on-itch-io" aria-label="Permalink to &quot;Web Games on itch.io&quot;"></a></h2>
<p>HTML5 and WebGL games have advantages on itch.io. Instant play with no download barrier. Mobile-friendly if you support touch. Easy to share with direct links. Great for jams because they're quick to try.</p>
<p>For optimization, loading time matters so keep the initial load small. Show a loading bar or percentage. Test on Chrome, Firefox, Safari, and Edge. Add touch support if feasible. Provide a fullscreen button.</p>
<p>For embed settings, use 960px width (or your game's native) and 540px height for 16:9. Enable mobile-friendly if responsive. Enable the fullscreen button. Disable scrollbars.</p>
<p>For performance, lazy-load assets, compress textures, minimize your JavaScript bundle, use WebGL efficiently, and test on lower-end devices.</p>
<h2 id="resources" tabindex="-1">Resources <a class="header-anchor" href="#resources" aria-label="Permalink to &quot;Resources&quot;"></a></h2>
<p>For official documentation, check the <a href="https://itch.io/docs/creators/" target="_blank" rel="noreferrer">itch.io Creator Docs</a>, <a href="https://itch.io/docs/creators/getting-indexed" target="_blank" rel="noreferrer">Getting Indexed guide</a>, and <a href="https://itch.io/docs/creators/pricing" target="_blank" rel="noreferrer">Pricing Guide</a>.</p>
<p>For communities, visit the <a href="https://itch.io/community" target="_blank" rel="noreferrer">itch.io Community Forums</a>, <a href="https://reddit.com/r/itchio" target="_blank" rel="noreferrer">r/itchio</a>, <a href="https://reddit.com/r/indiegaming" target="_blank" rel="noreferrer">r/indiegaming</a>, and <a href="https://reddit.com/r/IndieDev" target="_blank" rel="noreferrer">r/IndieDev</a>.</p>
<p>For tools, <a href="https://itch.io/docs/butler/" target="_blank" rel="noreferrer">itch.io Butler</a> is a CLI for uploads, and <a href="https://dopresskit.com/" target="_blank" rel="noreferrer">Presskit()</a> generates press kits.</p>
<h2 id="the-short-version" tabindex="-1">The Short Version <a class="header-anchor" href="#the-short-version" aria-label="Permalink to &quot;The Short Version&quot;"></a></h2>
<p>Before launch, make sure your game is playable and polished enough, your page has a great cover image, you have 3+ screenshots and ideally a GIF or trailer, your description is clear and scannable, all relevant tags are selected, you've decided on pricing, you've started your social media presence, and you've posted devlogs.</p>
<p>At launch, your page should be 100% complete, published publicly, announced everywhere, and you should be engaging with comments.</p>
<p>After launch, respond to feedback, fix bugs quickly, keep posting devlogs, and build toward your next release.</p>
<p>Most successful indie games took years and multiple releases to find their audience. Your first game probably won't be a hit, and that's fine. Each release teaches you, grows your audience, and brings you closer to the game that breaks through.</p>
<p>Ship it. Learn. Repeat.</p>
<hr>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="how-do-i-get-my-game-noticed-on-itch-io" tabindex="-1">How do I get my game noticed on itch.io? <a class="header-anchor" href="#how-do-i-get-my-game-noticed-on-itch-io" aria-label="Permalink to &quot;How do I get my game noticed on itch.io?&quot;"></a></h3>
<p>Tags are the biggest lever most people ignore. Itch.io's browse pages are organized by tags, and that's how most players find new stuff. Pick every relevant tag, not just the obvious ones. After that, it's devlogs. Each devlog post bumps your game in feeds and gives search engines more content to index. Participate in game jams too, they're the fastest way to get eyeballs when you're starting from zero. And make your cover image count. It's the first thing people see when scrolling, and most of them decide in under a second whether to click.</p>
<h3 id="can-you-make-money-on-itch-io" tabindex="-1">Can you make money on itch.io? <a class="header-anchor" href="#can-you-make-money-on-itch-io" aria-label="Permalink to &quot;Can you make money on itch.io?&quot;"></a></h3>
<p>You can, but set your expectations accordingly. Itch.io uses a &quot;pay what you want&quot; model by default, and the platform lets you keep up to 100% of revenue (you choose how much to share). Some developers do well with premium games priced at $5-15, especially after building an audience through free releases and jam entries. The platform won't make you rich overnight, but it's one of the few places where you keep full control of pricing and payouts with basically no gatekeeping.</p>
<h3 id="is-itch-io-good-for-browser-games" tabindex="-1">Is itch.io good for browser games? <a class="header-anchor" href="#is-itch-io-good-for-browser-games" aria-label="Permalink to &quot;Is itch.io good for browser games?&quot;"></a></h3>
<p>It's one of the best places for them, honestly. Browser games get played more on itch.io because there's no download friction. Players click, they're in. The platform supports HTML5/WebGL embeds natively, so your game shows up right on the page. For game jams especially, browser games get way more ratings than downloadable ones because judges can try them instantly. Keep your initial load under a few MB and show a progress bar, and you'll hold more players than you'd expect.</p>
<hr>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/game-jams-hackathons.html">Game Jams &amp; Hackathons</a> — jams are the fastest way to build an itch.io audience</li>
<li><a href="/tutorials/iframe-embedding-games.html">Iframe embedding games</a> — technical guide to embedding browser games in iframes</li>
<li><a href="/tutorials/ship-web-game-fast.html">Ship a web game that loads fast</a> — optimize load times for itch.io embeds</li>
<li><a href="/guides/steam-next-fest-strategy.html">Steam Next Fest Strategy</a> — if you're considering multi-platform launch</li>
<li><a href="/guides/web-game-engines-comparison.html">Web Game Engines Comparison</a> — picking an engine that exports clean web builds</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Three.js + USDC in the Browser]]></title>
            <link>https://app.cinevva.com/guides/threejs-usdc-tech-report</link>
            <guid>https://app.cinevva.com/guides/threejs-usdc-tech-report</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How to load USD binary files in Three.js without WASM or native code]]></description>
            <content:encoded><![CDATA[<h1 id="three-js-usdc-in-the-browser" tabindex="-1">Three.js + USDC in the Browser <a class="header-anchor" href="#three-js-usdc-in-the-browser" aria-label="Permalink to &quot;Three.js + USDC in the Browser&quot;"></a></h1>
<p>So you've got a <code>.usdc</code> file from Maya or Houdini, and you want to show it in a Three.js scene. I've been there. Until recently, your choices weren't great. You could convert to glTF offline, spin up a WASM build of OpenUSD, or just give up and use a different format.</p>
<p>This guide shows you how to parse USDC directly in JavaScript using <code>@cinevva/usdjs</code>, and how to get that data into Three.js.</p>
<h2 id="what-s-usdc" tabindex="-1">What's USDC? <a class="header-anchor" href="#what-s-usdc" aria-label="Permalink to &quot;What's USDC?&quot;"></a></h2>
<p>USD files come in three flavors. USDA is the text version, human-readable and easy to debug but verbose. USDC (sometimes called &quot;Crate&quot;) is the binary format that production pipelines actually use because it's compact and loads fast in native tools. USDZ is a zip archive containing USDC plus textures, which Apple uses for AR Quick Look.</p>
<p>Here's the catch: USDC is a proprietary binary format. Pixar wrote the reference implementation in C++, and until now there wasn't a pure JavaScript way to read it. That's starting to change: in December 2025 the Alliance for OpenUSD published Core Specification 1.0, the first ratified standard documenting how OpenUSD scene data is structured, composed, and exchanged, with a 1.1 revision already in progress. The reference implementation still lives in Pixar's C++ codebase, though, so a separate reader is still the practical way into the format from JavaScript.</p>
<h2 id="why-you-might-care" tabindex="-1">Why You Might Care <a class="header-anchor" href="#why-you-might-care" aria-label="Permalink to &quot;Why You Might Care&quot;"></a></h2>
<p>If you're building 3D web apps that need to work with content from film or VFX pipelines, you're going to run into USD. Artists export from Maya, Houdini, or Blender, and those exports are often USDC.</p>
<p>Before <code>@cinevva/usdjs</code>, you had three options. You could run <code>usdcat</code> to convert USDC to text format, which adds a build step and loses the binary format's compactness. You could compile OpenUSD or TinyUSDZ to WebAssembly, which adds megabytes to your bundle and needs special server headers for threading. Or you could use Three.js's built-in USDLoader, which handles USDZ but has limited USDC support and minimal composition.</p>
<p>Now there's a fourth option: parse USDC natively in JavaScript.</p>
<h2 id="how-it-works" tabindex="-1">How It Works <a class="header-anchor" href="#how-it-works" aria-label="Permalink to &quot;How It Works&quot;"></a></h2>
<p>The <code>@cinevva/usdjs</code> library reimplements USD's core functionality in TypeScript. I built the USDC parser by reading Pixar's C++ source code and translating the Crate format specification into JavaScript. When there's ambiguity in behavior, we match what OpenUSD does.</p>
<p>In practice, it looks like this:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { parseUsdcToLayer } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cinevva/usdjs'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Fetch the USDC file</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> response</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fetch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/model.usdc'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> buffer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> response.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">arrayBuffer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Parse it</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> layer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> parseUsdcToLayer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(buffer, { identifier: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'model.usdc'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Now you have a structured layer with prims, properties, and metadata</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">log</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(layer.pseudoRoot.children);</span></span></code></pre>
</div><p>No WASM. No native code. Just JavaScript reading bytes and building a structured representation.</p>
<h2 id="wiring-it-into-three-js" tabindex="-1">Wiring It Into Three.js <a class="header-anchor" href="#wiring-it-into-three-js" aria-label="Permalink to &quot;Wiring It Into Three.js&quot;"></a></h2>
<p>Parsing USD is only half the problem. You also need to convert USD concepts (prims, transforms, mesh schemas) into Three.js objects.</p>
<p>Here's a minimal example that loads a USDC file and creates Three.js meshes:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> *</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> as</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> THREE </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'three'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { UsdStage, parseUsdcToLayer } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cinevva/usdjs'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> loadUsdcToThree</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">scene</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> THREE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Scene</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 1. Fetch and parse</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> buffer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fetch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(url).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">then</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">r</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> r.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">arrayBuffer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> layer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> parseUsdcToLayer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(buffer, { identifier: url });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 2. Create a stage (handles composition if there are references)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> stage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UsdStage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">open</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(layer);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // 3. Walk the prim tree</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> prim</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> stage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">traverse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Skip non-geometry</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (prim.typeName </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!==</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'Mesh'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">continue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Get geometry data</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> points</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> prim.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAttribute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'points'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.value;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> faceVertexIndices</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> prim.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAttribute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'faceVertexIndices'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.value;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> faceVertexCounts</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> prim.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAttribute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'faceVertexCounts'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.value;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">points </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">faceVertexIndices </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">||</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">faceVertexCounts) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">continue</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Convert to Three.js geometry</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> geometry</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> THREE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">BufferGeometry</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    geometry.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setAttribute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'position'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      new</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> THREE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Float32BufferAttribute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(points.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flat</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">3</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // USD uses faceVertexCounts + faceVertexIndices, Three.js wants a flat index array</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // For triangulated meshes, this is straightforward:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    geometry.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setIndex</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(Array.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">from</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(faceVertexIndices));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    geometry.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">computeVertexNormals</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Create mesh</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> material</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> THREE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">MeshStandardMaterial</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ color: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0x888888</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> mesh</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> THREE</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Mesh</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(geometry, material);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Apply transform</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> xform</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getWorldTransform</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(prim);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    mesh.matrix.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">fromArray</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(xform);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    mesh.matrixAutoUpdate </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    scene.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(mesh);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getWorldTransform</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">prim</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[] {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // Simplified: in practice you'd compose parent transforms</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> xformOp</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> prim.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getAttribute</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'xformOp:transform'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (xformOp?.value) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> xformOp.value.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flat</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>This example is simplified. Real code needs to handle transform stacking (USD prims can have multiple <code>xformOp</code> attributes that compose together), subdivision surfaces (meshes with <code>catmullClark</code> subdivision need actual subdivision), materials (<code>UsdPreviewSurface</code> parameters map to Three.js <code>MeshStandardMaterial</code>), textures (asset paths need resolution and loading), and skeletal animation (<code>UsdSkel</code> bindings need conversion to <code>SkinnedMesh</code>).</p>
<p>If you want to see how all of this works together, check out <code>@cinevva/usdjs-viewer</code>. It's a complete reference implementation.</p>
<h2 id="composition-matters-more-than-you-d-think" tabindex="-1">Composition Matters More Than You'd Think <a class="header-anchor" href="#composition-matters-more-than-you-d-think" aria-label="Permalink to &quot;Composition Matters More Than You'd Think&quot;"></a></h2>
<p>Parsing a single USDC file is the easy part. Real USD scenes use composition, which means sublayers, references, payloads, and variants all working together.</p>
<p>Picture a car model. The body references <code>body.usdc</code>, the wheels reference <code>wheel.usdc</code>, and there are variants for &quot;sport&quot; and &quot;sedan&quot; configurations. The final scene is assembled from all these pieces at runtime.</p>
<p>Here's how you handle that:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { UsdStage, parseUsdcToLayer, FetchResolver } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cinevva/usdjs'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Create a resolver that knows how to fetch referenced files</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> resolver</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> FetchResolver</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  baseUrl: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/assets/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Open with composition</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> stage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> UsdStage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">open</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(rootLayer, { resolver });</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Set a variant selection</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">stage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setVariantSelection</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/Car'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'bodyStyle'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'sport'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Now traverse the composed scene</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">for</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> prim</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> of</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> stage.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">traverse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // You'll see resolved references and the selected variant</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>The resolver fetches referenced USDC files on demand. Composition happens in JavaScript, producing the flattened scene you'd get from <code>usdcat --flatten</code>.</p>
<h2 id="performance" tabindex="-1">Performance <a class="header-anchor" href="#performance" aria-label="Permalink to &quot;Performance&quot;"></a></h2>
<p>Let's be honest: parsing USDC in JavaScript is slower than native C++. That's the trade-off for skipping WASM and build steps.</p>
<p>In practice, for typical web use cases with models under 10MB and less than 100K triangles, parsing takes 50-200ms on modern hardware. That's fine for initial load if you show a loading indicator.</p>
<p>You can make it faster. Don't load the entire scene upfront. Load the root layer, render what you can, then fetch payloads on demand. Move parsing to a Web Worker so the UI stays responsive. Cache parsed layers in IndexedDB so return visits are instant. Show a low-res preview while the full scene streams in.</p>
<p>Here's a Web Worker setup:</p>
<div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// worker.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { parseUsdcToLayer } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cinevva/usdjs'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">self.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">onmessage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">buffer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">identifier</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e.data;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> layer</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> parseUsdcToLayer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(buffer, { identifier });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // Serialize layer data (not the methods)</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> serializeLayer</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(layer);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  self.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">postMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// main.ts</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> worker</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Worker</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> URL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'./worker.ts'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">meta</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.url));</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">worker.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">postMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({ buffer, identifier: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'model.usdc'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">worker.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">onmessage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">e</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> layerData</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> e.data;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // Build Three.js scene from layerData</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre>
</div><h2 id="what-works-and-what-doesn-t" tabindex="-1">What Works and What Doesn't <a class="header-anchor" href="#what-works-and-what-doesn-t" aria-label="Permalink to &quot;What Works and What Doesn't&quot;"></a></h2>
<p>Most of the common stuff works. Geometry (meshes, points, curves) parses correctly, including compressed vertex arrays. Transforms work with full <code>xformOp</code> stacking. Materials map from <code>UsdPreviewSurface</code> to PBR. Composition handles sublayers, references, payloads, variants, and inherits. Textures resolve and load if you provide a resolver. Basic skeletal animation works for common character rigs.</p>
<p>There are gaps though. This isn't a Hydra implementation, so you're responsible for converting USD data to whatever rendering engine you're using. There are no typed schema APIs like <code>UsdGeomMesh</code> with convenience methods. You work with generic prims and attributes. Some composition features like specializes, relocates, and value clips aren't implemented yet. Complex material networks beyond simple <code>UsdPreviewSurface</code> need custom handling.</p>
<h2 id="when-to-use-this" tabindex="-1">When to Use This <a class="header-anchor" href="#when-to-use-this" aria-label="Permalink to &quot;When to Use This&quot;"></a></h2>
<p>This makes sense when you need to load USD files in a web app without a WASM build step, when your pipeline outputs USDC and you don't want to convert to glTF, when you're building a USD viewer or editor for the browser, or when you want to inspect USD structure rather than just render it.</p>
<p>Use something else if you need full OpenUSD parity with every edge case, if your scenes are huge (100MB+) and need native performance, or if you're already using a WASM build and the complexity is acceptable for your project.</p>
<h2 id="resources" tabindex="-1">Resources <a class="header-anchor" href="#resources" aria-label="Permalink to &quot;Resources&quot;"></a></h2>
<p>The three packages are <code>@cinevva/usdjs</code> for core parsing and composition, <code>@cinevva/usdjs-viewer</code> for a Three.js-based browser viewer, and <code>@cinevva/usdjs-renderer</code> for headless PNG rendering in tests.</p>
<p>For documentation, check the <a href="https://cinevva-engine.github.io/usdjs/API" target="_blank" rel="noreferrer">usdjs API Reference</a>, the <a href="https://openusd.org/release/index.html" target="_blank" rel="noreferrer">Pixar OpenUSD Specification</a>, and the <a href="https://threejs.org/docs/" target="_blank" rel="noreferrer">Three.js Documentation</a>.</p>
<p>If you want to understand how the USDC parser maps to Pixar's implementation, look at <code>src/usdc/PIXAR_PARITY.md</code> in the usdjs repo.</p>
<h2 id="try-it" tabindex="-1">Try It <a class="header-anchor" href="#try-it" aria-label="Permalink to &quot;Try It&quot;"></a></h2>
<p>The fastest way to see this working is to visit the <a href="https://cinevva-engine.github.io/usdjs-viewer/" target="_blank" rel="noreferrer">usdjs-viewer demo</a> and drop a USDC file onto it.</p>
<p>To integrate into your own project, install the package and start with the code examples above:</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> @cinevva/usdjs</span></span></code></pre>
</div><p>USD in the browser is possible. It's not always the right choice, but when you need it, pure JavaScript parsing means one less build step and one less dependency to think about.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/tutorials/webgl-fundamentals-for-games.html">WebGL fundamentals for game developers</a> — the rendering API powering Three.js scenes</li>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> — where Three.js fits in the WebGL/WebGPU/Wasm landscape</li>
<li><a href="/guides/browser-3d-open-world-tech.html">Browser 3D Open World Tech</a> — streaming 3D assets including USD for open worlds</li>
<li><a href="/guides/game-assets-guide.html">Where to Find Free Game Assets</a> — sources for 3D models compatible with Three.js</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[11 Best Web Game Engines for 2026, Ranked and Compared]]></title>
            <link>https://app.cinevva.com/guides/web-game-engines-comparison</link>
            <guid>https://app.cinevva.com/guides/web-game-engines-comparison</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[We compared 11 web game engines on build size, load time, 2D vs 3D, WebGPU, and cost. See which one wins for your project, from Unity and Godot to PlayCanvas, Defold, and Phaser.]]></description>
            <content:encoded><![CDATA[<h1 id="best-web-game-engines-for-2026-compared" tabindex="-1">Best Web Game Engines for 2026 (Compared) <a class="header-anchor" href="#best-web-game-engines-for-2026-compared" aria-label="Permalink to &quot;Best Web Game Engines for 2026 (Compared)&quot;"></a></h1>
<p><em>Last updated: June 2026.</em></p>
<p>Building a game for the web? Your choice of engine determines everything: build size, load time, visual fidelity, and how much of your audience can actually play.</p>
<p>This guide compares the best web game engines available in 2026, from full-featured engines like Unity and Godot to lightweight JavaScript frameworks like Phaser and PixiJS.</p>
<h2 id="best-web-game-engines-for-2026-quick-picks" tabindex="-1">Best Web Game Engines for 2026: Quick Picks <a class="header-anchor" href="#best-web-game-engines-for-2026-quick-picks" aria-label="Permalink to &quot;Best Web Game Engines for 2026: Quick Picks&quot;"></a></h2>
<p>Short on time? Here's where each engine wins in 2026.</p>
<h3 id="best-3d-web-game-engines" tabindex="-1">Best 3D web game engines <a class="header-anchor" href="#best-3d-web-game-engines" aria-label="Permalink to &quot;Best 3D web game engines&quot;"></a></h3>
<p>PlayCanvas leads for browser-first 3D: a 1-2 MB runtime, WebGPU in beta, and a collaborative cloud editor. Godot (Compatibility renderer) and Cocos Creator are strong free alternatives, and Unity WebGL works if you also need native builds and can accept an 8 MB+ download.</p>
<h3 id="top-2d-game-engines-for-2026" tabindex="-1">Top 2D game engines for 2026 <a class="header-anchor" href="#top-2d-game-engines-for-2026" aria-label="Permalink to &quot;Top 2D game engines for 2026&quot;"></a></h3>
<p>Defold and Phaser are the picks for 2D. Defold ships the smallest builds (~1.14 MB) and loads fastest; Phaser gives you a full batteries-included framework with the biggest community. For non-coders, Construct and GDevelop build 2D games visually with no programming. For the full breakdown, see <a href="/guides/best-2d-game-engines-2026.html">best 2D game engines for 2026</a>.</p>
<h3 id="best-wasm-game-engines" tabindex="-1">Best Wasm game engines <a class="header-anchor" href="#best-wasm-game-engines" aria-label="Permalink to &quot;Best Wasm game engines&quot;"></a></h3>
<p>If WebAssembly performance is the priority, Godot (Wasm + SIMD by default), Defold (Wasm runtime), and Cocos Creator (Wasm physics with 5-9x gains) compile to fast, compact web builds. Wasm is what lets these engines run near-native speed inside the browser.</p>
<h3 id="best-free-and-open-source-web-game-engines" tabindex="-1">Best free and open-source web game engines <a class="header-anchor" href="#best-free-and-open-source-web-game-engines" aria-label="Permalink to &quot;Best free and open-source web game engines&quot;"></a></h3>
<p>Godot, Phaser, PixiJS, Defold, and Excalibur.js are all free and open-source, so there are no license fees or revenue thresholds. Godot is the most complete free engine for both 2D and 3D.</p>
<h2 id="quick-reference-table" tabindex="-1">Quick Reference Table <a class="header-anchor" href="#quick-reference-table" aria-label="Permalink to &quot;Quick Reference Table&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Engine</th>
<th>Type</th>
<th>Web Tech</th>
<th>Build Size (empty)</th>
<th>Best For</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://unity.com/" target="_blank" rel="noreferrer"><strong>Unity</strong></a></td>
<td>Full engine</td>
<td>WebGL</td>
<td>~8 MB</td>
<td>3D games, cross-platform</td>
<td>Free &lt; $200K; Pro $2,200/yr</td>
</tr>
<tr>
<td><a href="https://godotengine.org/" target="_blank" rel="noreferrer"><strong>Godot</strong></a></td>
<td>Full engine</td>
<td>WebGL + Wasm</td>
<td>~9 MB (24 MB uncompressed)</td>
<td>2D/3D indie games</td>
<td>Free (MIT)</td>
</tr>
<tr>
<td><a href="https://www.unrealengine.com/" target="_blank" rel="noreferrer"><strong>Unreal</strong></a></td>
<td>Full engine</td>
<td>Pixel Streaming only</td>
<td>N/A (server-side)</td>
<td>High-fidelity visualization</td>
<td>Free &lt; $1M revenue</td>
</tr>
<tr>
<td><a href="https://www.cocos.com/en/creator" target="_blank" rel="noreferrer"><strong>Cocos Creator</strong></a></td>
<td>Full engine</td>
<td>WebGL + WebGPU + Wasm</td>
<td>~2-4 MB</td>
<td>2D/3D, mini-games (Asia)</td>
<td>Free</td>
</tr>
<tr>
<td><a href="https://playcanvas.com/" target="_blank" rel="noreferrer"><strong>PlayCanvas</strong></a></td>
<td>Web-first 3D</td>
<td>WebGL2 + WebGPU</td>
<td>~1-2 MB</td>
<td>Interactive 3D, web apps</td>
<td>Free tier; paid plans</td>
</tr>
<tr>
<td><a href="https://defold.com/" target="_blank" rel="noreferrer"><strong>Defold</strong></a></td>
<td>Web-first 2D/3D</td>
<td>WebGL + Wasm</td>
<td>~1.14 MB</td>
<td>Casual/mobile web games</td>
<td>Free</td>
</tr>
<tr>
<td><a href="https://www.construct.net/" target="_blank" rel="noreferrer"><strong>Construct</strong></a></td>
<td>Visual 2D</td>
<td>WebGL + WebGPU</td>
<td>~1-2 MB</td>
<td>2D games, non-coders</td>
<td>Subscription</td>
</tr>
<tr>
<td><a href="https://gdevelop.io/" target="_blank" rel="noreferrer"><strong>GDevelop</strong></a></td>
<td>Visual 2D/3D</td>
<td>WebGL</td>
<td>~2-3 MB</td>
<td>Beginners, rapid prototyping</td>
<td>Free tier; paid plans</td>
</tr>
<tr>
<td><a href="https://phaser.io/" target="_blank" rel="noreferrer"><strong>Phaser</strong></a></td>
<td>JS framework</td>
<td>WebGL/Canvas</td>
<td>~500 KB</td>
<td>2D games, rapid prototyping</td>
<td>Free (MIT)</td>
</tr>
<tr>
<td><a href="https://pixijs.com/" target="_blank" rel="noreferrer"><strong>PixiJS</strong></a></td>
<td>JS renderer</td>
<td>WebGL</td>
<td>~200 KB</td>
<td>Custom 2D rendering</td>
<td>Free (MIT)</td>
</tr>
<tr>
<td><a href="https://excaliburjs.com/" target="_blank" rel="noreferrer"><strong>Excalibur.js</strong></a></td>
<td>JS framework</td>
<td>WebGL</td>
<td>~300 KB</td>
<td>TypeScript 2D games</td>
<td>Free (BSD)</td>
</tr>
</tbody>
</table>
<h2 id="full-featured-game-engines" tabindex="-1">Full-Featured Game Engines <a class="header-anchor" href="#full-featured-game-engines" aria-label="Permalink to &quot;Full-Featured Game Engines&quot;"></a></h2>
<h3 id="unity-webgl" tabindex="-1"><a href="https://unity.com/" target="_blank" rel="noreferrer">Unity</a> (WebGL) <a class="header-anchor" href="#unity-webgl" aria-label="Permalink to &quot;[Unity](https://unity.com/) (WebGL)&quot;"></a></h3>
<p>The industry workhorse. Unity's WebGL export produces playable web builds, but with significant trade-offs.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Mature ecosystem with extensive Asset Store</li>
<li>Advanced rendering, physics, animation tools</li>
<li>Strong documentation and community</li>
<li>Cross-platform: same codebase deploys to desktop, mobile, console, and web</li>
</ul>
<p><strong>Web Limitations:</strong></p>
<ul>
<li>Large build sizes (~8+ MB minimum)</li>
<li>Slower load times compared to web-native engines</li>
<li>Performance lags behind native builds</li>
<li>WebGPU backend is experimental (Unity 6.1+ / 6.3 LTS), off by default and not yet production-ready</li>
<li>Memory overhead can be problematic on mobile browsers</li>
</ul>
<p><strong>Pricing (2026):</strong></p>
<ul>
<li>Personal: Free up to $200K revenue</li>
<li>Pro: ~$2,200/seat/year</li>
<li>Runtime fee cancelled in late 2024</li>
</ul>
<p><strong>When to use:</strong> You're building a complex 3D game that also needs to ship on other platforms. Web is secondary.</p>
<p><strong>Links:</strong> <a href="https://unity.com/" target="_blank" rel="noreferrer">Unity</a> · <a href="https://docs.unity3d.com/Manual/webgl.html" target="_blank" rel="noreferrer">WebGL Docs</a> · <a href="https://assetstore.unity.com/" target="_blank" rel="noreferrer">Asset Store</a></p>
<hr>
<h3 id="godot-html5" tabindex="-1"><a href="https://godotengine.org/" target="_blank" rel="noreferrer">Godot</a> (HTML5) <a class="header-anchor" href="#godot-html5" aria-label="Permalink to &quot;[Godot](https://godotengine.org/) (HTML5)&quot;"></a></h3>
<p>Open-source and rapidly improving. Godot 4.5+ includes WebAssembly SIMD by default, boosting performance without code changes.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Completely free and open-source (MIT license)</li>
<li>Lightweight editor</li>
<li>Excellent 2D support with dedicated renderer</li>
<li>GDScript is beginner-friendly</li>
<li>WebAssembly + SIMD for better performance</li>
<li>Active, growing community</li>
</ul>
<p><strong>Web Limitations:</strong></p>
<ul>
<li>C# projects cannot export to web (GDScript only)</li>
<li>WebGPU backend not finished</li>
<li>Only Compatibility renderer (WebGL 2.0) available for web</li>
<li>Safari/iOS support is problematic</li>
<li>Empty builds ~24 MB uncompressed (~9 MB gzipped)</li>
<li>Some 3D rendering features lag behind Unity</li>
</ul>
<p><strong>Threading:</strong></p>
<ul>
<li>Requires COOP/COEP headers for SharedArrayBuffer</li>
<li>Single-threaded is now the default (avoids header complexity)</li>
</ul>
<p><strong>When to use:</strong> You want open-source, prioritize 2D, or want full control without licensing concerns.</p>
<p><strong>Links:</strong> <a href="https://godotengine.org/" target="_blank" rel="noreferrer">Godot Engine</a> · <a href="https://docs.godotengine.org/en/stable/tutorials/export/exporting_for_web.html" target="_blank" rel="noreferrer">Web Export Docs</a> · <a href="https://github.com/godotengine/godot" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h3 id="unreal-engine-5" tabindex="-1"><a href="https://www.unrealengine.com/" target="_blank" rel="noreferrer">Unreal Engine 5</a> <a class="header-anchor" href="#unreal-engine-5" aria-label="Permalink to &quot;[Unreal Engine 5](https://www.unrealengine.com/)&quot;"></a></h3>
<p>Unreal dropped native HTML5 export with UE 4.24. In UE5, browser delivery requires <strong>Pixel Streaming</strong>.</p>
<p><strong>How Pixel Streaming Works:</strong></p>
<ul>
<li>Game runs on a server with GPU</li>
<li>Frames streamed to browser via WebRTC</li>
<li>Input sent back from browser</li>
<li>One UE instance per user typically required</li>
</ul>
<p><strong>Strengths:</strong></p>
<ul>
<li>Full UE5 visual fidelity in browser</li>
<li>No client-side hardware requirements</li>
<li>Works on any device with a browser</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>Requires server infrastructure (expensive to scale)</li>
<li>Latency depends on network</li>
<li>Video compression artifacts</li>
<li>Not suitable for traditional &quot;web games&quot;—more for visualizations, configurators, showcases</li>
</ul>
<p><strong>Community HTML5 efforts exist</strong> (WebAssembly/WebGPU forks like SimplyStream) but are experimental and limited.</p>
<p><strong>When to use:</strong> High-fidelity architectural visualization or product configurators where server costs are acceptable. Not practical for games targeting mass web audiences.</p>
<p><strong>Links:</strong> <a href="https://www.unrealengine.com/" target="_blank" rel="noreferrer">Unreal Engine</a> · <a href="https://dev.epicgames.com/documentation/en-us/unreal-engine/pixel-streaming-in-unreal-engine" target="_blank" rel="noreferrer">Pixel Streaming Docs</a></p>
<hr>
<h3 id="cocos-creator" tabindex="-1">Cocos Creator <a class="header-anchor" href="#cocos-creator" aria-label="Permalink to &quot;Cocos Creator&quot;"></a></h3>
<p>A full-featured engine with exceptional web and mini-game platform support, particularly popular in Asia.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Dual-core architecture: Web3D core (TypeScript) optimized for web/mini-games</li>
<li>WebGPU support (added in 3.8.4)</li>
<li>WebAssembly for physics (Box2D, Bullet) with 5-9× performance gains</li>
<li>Excellent mini-game platform support (WeChat, TikTok, Taobao)</li>
<li>Custom Render Pipeline (CRP) for optimization control</li>
<li>Spine 4.2 support with WASM runtime</li>
<li>Package size trimming and Brotli compression</li>
<li>Completely free</li>
</ul>
<p><strong>Web Limitations:</strong></p>
<ul>
<li>Smaller Western community (documentation primarily in Chinese)</li>
<li>Initial load can be heavy with all features enabled</li>
<li>Fallback to WebGL1/asm.js on older browsers degrades performance</li>
</ul>
<p><strong>Recent additions (2025-2026):</strong></p>
<ul>
<li>WebGPU backend</li>
<li>WASM physics modules by default</li>
<li>Custom Render Pipeline via RenderGraph</li>
<li>ASTC texture compression for mini-games</li>
<li>Significant package size reductions</li>
</ul>
<p><strong>When to use:</strong> Mini-game platforms (WeChat, TikTok), mobile web games targeting Asian markets, or when you need strong 2D/3D web performance with a free engine.</p>
<p><strong>Links:</strong> <a href="https://www.cocos.com/en/creator" target="_blank" rel="noreferrer">Cocos Creator</a> · <a href="https://docs.cocos.com/creator/manual/en/" target="_blank" rel="noreferrer">Documentation</a> · <a href="https://github.com/cocos/cocos-engine" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h2 id="web-first-engines" tabindex="-1">Web-First Engines <a class="header-anchor" href="#web-first-engines" aria-label="Permalink to &quot;Web-First Engines&quot;"></a></h2>
<p>These engines are built specifically for web delivery.</p>
<h3 id="playcanvas" tabindex="-1"><a href="https://playcanvas.com/" target="_blank" rel="noreferrer">PlayCanvas</a> <a class="header-anchor" href="#playcanvas" aria-label="Permalink to &quot;[PlayCanvas](https://playcanvas.com/)&quot;"></a></h3>
<p>A cloud-based 3D engine designed for the web from the ground up.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>WebGL2 + WebGPU (beta) support</li>
<li>Collaborative cloud editor</li>
<li>Small runtime (~1-2 MB)</li>
<li>PBR rendering, post-processing, GPU particles</li>
<li>Compute shaders via WebGPU</li>
<li>3D Gaussian Splatting support</li>
<li>Efficient asset compression (Basis Universal, WebP)</li>
</ul>
<p><strong>Recent additions (2025-2026):</strong></p>
<ul>
<li>Engine V2 with WebGPU support</li>
<li>Compute shaders</li>
<li>Multi-draw for reduced draw call overhead</li>
<li>HDR support in viewer</li>
<li>Improved glTF export</li>
<li>Engine v2.19.0 (June 2026) added a new high-performance WebGPU renderer, including a compute-based WebGPU path for 3D Gaussian splats</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>WebGPU still in beta (some features missing like runtime lightmapping)</li>
<li>Cloud-based workflow may not suit all teams</li>
<li>Less suited for complex 2D games</li>
</ul>
<p><strong>Pricing:</strong></p>
<ul>
<li>Free tier available</li>
<li>Paid plans for private projects, storage, support</li>
</ul>
<p><strong>When to use:</strong> Interactive 3D web experiences, product configurators, web-based games where small download size matters.</p>
<p><strong>Links:</strong> <a href="https://playcanvas.com/" target="_blank" rel="noreferrer">PlayCanvas</a> · <a href="https://developer.playcanvas.com/" target="_blank" rel="noreferrer">Documentation</a> · <a href="https://github.com/playcanvas/engine" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h3 id="defold" tabindex="-1"><a href="https://defold.com/" target="_blank" rel="noreferrer">Defold</a> <a class="header-anchor" href="#defold" aria-label="Permalink to &quot;[Defold](https://defold.com/)&quot;"></a></h3>
<p>A lightweight engine from King (Candy Crush) with exceptional web performance.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Tiny runtime: ~1.14 MB (gzipped, empty build)</li>
<li>WebAssembly by default</li>
<li>Excellent load times</li>
<li>Built-in publishing to Poki and other platforms</li>
<li>HTML5 performance profiler</li>
<li>GPU skinning with 30-50% performance improvements in recent versions</li>
<li>WebGPU experimental support</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>Smaller community than Godot/Unity</li>
<li>Debug builds significantly slower</li>
<li>256 MB default heap (needs tuning for larger games)</li>
<li>Learning curve for Lua scripting</li>
</ul>
<p><strong>Performance comparison:</strong></p>
<ul>
<li>Empty build: Defold ~1.14 MB vs Unity ~8 MB vs Godot ~9 MB</li>
<li>Among the fastest-loading engines in studies of top web games</li>
</ul>
<p><strong>When to use:</strong> Mobile-first web games, casual games, any project where load time is critical.</p>
<p><strong>Links:</strong> <a href="https://defold.com/" target="_blank" rel="noreferrer">Defold</a> · <a href="https://defold.com/manuals/html5/" target="_blank" rel="noreferrer">HTML5 Docs</a> · <a href="https://github.com/defold/defold" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h3 id="construct" tabindex="-1"><a href="https://www.construct.net/" target="_blank" rel="noreferrer">Construct</a> <a class="header-anchor" href="#construct" aria-label="Permalink to &quot;[Construct](https://www.construct.net/)&quot;"></a></h3>
<p>Visual event-based development. Perfect for non-programmers.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>No coding required (event sheets)</li>
<li>WebGPU renderer enabled by default (where supported)</li>
<li>Rapid iteration and prototyping</li>
<li>55 releases in 2025 alone</li>
<li>TypeScript support in editor</li>
<li>Built-in asset browser, debugger improvements</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>2D focused (minimal 3D support)</li>
<li>Subscription model</li>
<li>Less control than code-based engines</li>
<li>May hit walls on complex game logic</li>
</ul>
<p><strong>When to use:</strong> 2D games, rapid prototyping, education, or if you're not a programmer.</p>
<p><strong>Links:</strong> <a href="https://www.construct.net/" target="_blank" rel="noreferrer">Construct</a> · <a href="https://www.construct.net/en/make-games/manuals/construct-3" target="_blank" rel="noreferrer">Documentation</a></p>
<hr>
<h3 id="gdevelop" tabindex="-1"><a href="https://gdevelop.io/" target="_blank" rel="noreferrer">GDevelop</a> <a class="header-anchor" href="#gdevelop" aria-label="Permalink to &quot;[GDevelop](https://gdevelop.io/)&quot;"></a></h3>
<p>Open-source visual game engine with growing 3D support.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Event-based, no-code development</li>
<li>Native 3D support (added in 5.3)</li>
<li>Real-time 3D editor (5.6)</li>
<li>Jolt Physics for 3D</li>
<li>Built-in profiler</li>
<li>Real-time multiplayer support (2-8 players)</li>
<li>Free tier available</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>Exported code includes engine libraries (Pixi.js, Three.js, Howler.js)</li>
<li>Build size larger than minimal frameworks</li>
<li>Editor can slow with many objects/scenes</li>
<li>Performance depends heavily on optimization techniques</li>
</ul>
<p><strong>When to use:</strong> Beginners, educational contexts, rapid prototyping, smaller 2D/3D games.</p>
<p><strong>Links:</strong> <a href="https://gdevelop.io/" target="_blank" rel="noreferrer">GDevelop</a> · <a href="https://wiki.gdevelop.io/" target="_blank" rel="noreferrer">Documentation</a> · <a href="https://github.com/4ian/GDevelop" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h2 id="javascript-frameworks" tabindex="-1">JavaScript Frameworks <a class="header-anchor" href="#javascript-frameworks" aria-label="Permalink to &quot;JavaScript Frameworks&quot;"></a></h2>
<p>For developers who want maximum control and minimal overhead.</p>
<h3 id="phaser-v4" tabindex="-1"><a href="https://phaser.io/" target="_blank" rel="noreferrer">Phaser</a> (v4) <a class="header-anchor" href="#phaser-v4" aria-label="Permalink to &quot;[Phaser](https://phaser.io/) (v4)&quot;"></a></h3>
<p>The most popular HTML5 game framework. Version 4 shipped stable in April 2026 (v4.0.0 &quot;Caladan&quot;), a ground-up rebuild of the WebGL renderer with a new node-based render architecture while keeping the v3 API. The v4.1.0 &quot;Salusa&quot; follow-up landed later the same month.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Full game framework: physics, input, audio, scenes, asset loading</li>
<li>Arcade Physics and Matter.js built-in</li>
<li>Massive community, tutorials, examples</li>
<li>WebGL + Canvas renderers</li>
<li>Templates for bundlers and desktop wrappers</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>Larger bundle than minimal libraries (~500 KB)</li>
<li>Performance in sprite-heavy scenarios lags PixiJS</li>
<li>More opinionated architecture</li>
</ul>
<p><strong>When to use:</strong> Standard 2D games where you want batteries-included but still write code.</p>
<p><strong>Links:</strong> <a href="https://phaser.io/" target="_blank" rel="noreferrer">Phaser</a> · <a href="https://phaser.io/learn" target="_blank" rel="noreferrer">Documentation</a> · <a href="https://github.com/phaserjs/phaser" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h3 id="pixijs-v8" tabindex="-1"><a href="https://pixijs.com/" target="_blank" rel="noreferrer">PixiJS</a> (v8) <a class="header-anchor" href="#pixijs-v8" aria-label="Permalink to &quot;[PixiJS](https://pixijs.com/) (v8)&quot;"></a></h3>
<p>High-performance 2D rendering library.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Exceptional rendering performance</li>
<li>Tiny footprint (~200 KB)</li>
<li>Powerful WebGL rendering pipeline</li>
<li>Great for custom rendering, visualizations</li>
<li>New &quot;gl2D&quot; scene format and game engine library in development</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>Not a game engine—no physics, audio, scene management</li>
<li>More setup required</li>
<li>You build (or integrate) game logic yourself</li>
</ul>
<p><strong>When to use:</strong> Graphics-heavy applications, custom rendering needs, performance-critical projects, or when building your own engine.</p>
<p><strong>Links:</strong> <a href="https://pixijs.com/" target="_blank" rel="noreferrer">PixiJS</a> · <a href="https://pixijs.com/guides" target="_blank" rel="noreferrer">Documentation</a> · <a href="https://github.com/pixijs/pixijs" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h3 id="excalibur-js" tabindex="-1"><a href="https://excaliburjs.com/" target="_blank" rel="noreferrer">Excalibur.js</a> <a class="header-anchor" href="#excalibur-js" aria-label="Permalink to &quot;[Excalibur.js](https://excaliburjs.com/)&quot;"></a></h3>
<p>TypeScript-first 2D game engine with ECS architecture.</p>
<p><strong>Strengths:</strong></p>
<ul>
<li>Built in TypeScript</li>
<li>Entity-Component-System architecture</li>
<li>Tilemaps, nine-slice sprites, GPU particles</li>
<li>HTML-based UI that scales with canvas</li>
<li>Good balance of features and control</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li>Pre-1.0 (some APIs may change)</li>
<li>Smaller ecosystem and fewer tutorials</li>
<li>Physics and lighting still being built out</li>
</ul>
<p><strong>When to use:</strong> TypeScript developers building 2D games who want more structure than PixiJS but less overhead than Phaser.</p>
<p><strong>Links:</strong> <a href="https://excaliburjs.com/" target="_blank" rel="noreferrer">Excalibur.js</a> · <a href="https://excaliburjs.com/docs/" target="_blank" rel="noreferrer">Documentation</a> · <a href="https://github.com/excaliburjs/Excalibur" target="_blank" rel="noreferrer">GitHub</a></p>
<hr>
<h2 id="decision-guide" tabindex="-1">Decision Guide <a class="header-anchor" href="#decision-guide" aria-label="Permalink to &quot;Decision Guide&quot;"></a></h2>
<h3 id="by-project-type" tabindex="-1">By Project Type <a class="header-anchor" href="#by-project-type" aria-label="Permalink to &quot;By Project Type&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Project</th>
<th>Recommended Engine</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Casual 2D mobile web game</strong></td>
<td>Defold, Phaser, Construct</td>
</tr>
<tr>
<td><strong>Complex 2D with custom needs</strong></td>
<td>PixiJS, Excalibur.js</td>
</tr>
<tr>
<td><strong>3D web game</strong></td>
<td>PlayCanvas, Godot (Compatibility), Cocos Creator</td>
</tr>
<tr>
<td><strong>Cross-platform (web + native)</strong></td>
<td>Unity, Godot, Cocos Creator</td>
</tr>
<tr>
<td><strong>Mini-game platforms (WeChat, TikTok)</strong></td>
<td>Cocos Creator</td>
</tr>
<tr>
<td><strong>High-fidelity visualization</strong></td>
<td>Unreal (Pixel Streaming), PlayCanvas</td>
</tr>
<tr>
<td><strong>No-code / beginner</strong></td>
<td>Construct, GDevelop</td>
</tr>
<tr>
<td><strong>Open-source required</strong></td>
<td>Godot, Phaser, PixiJS, Excalibur.js, Cocos (engine)</td>
</tr>
</tbody>
</table>
<h3 id="by-priority" tabindex="-1">By Priority <a class="header-anchor" href="#by-priority" aria-label="Permalink to &quot;By Priority&quot;"></a></h3>
<table tabindex="0">
<thead>
<tr>
<th>Priority</th>
<th>Best Options</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Smallest build size</strong></td>
<td>Defold, PixiJS, Excalibur.js</td>
</tr>
<tr>
<td><strong>Fastest load time</strong></td>
<td>Defold, Phaser, vanilla JS</td>
</tr>
<tr>
<td><strong>Best 3D support</strong></td>
<td>PlayCanvas, Unity, Cocos Creator</td>
</tr>
<tr>
<td><strong>Best 2D support</strong></td>
<td>Godot, Phaser, Construct, Cocos Creator</td>
</tr>
<tr>
<td><strong>Free &amp; open-source</strong></td>
<td>Godot, Phaser, PixiJS, Defold, Cocos (engine)</td>
</tr>
<tr>
<td><strong>WebGPU support</strong></td>
<td>PlayCanvas (beta), Construct, Cocos Creator</td>
</tr>
<tr>
<td><strong>Mobile browser performance</strong></td>
<td>Defold, Phaser, PlayCanvas, Cocos Creator</td>
</tr>
<tr>
<td><strong>Mini-game platforms</strong></td>
<td>Cocos Creator</td>
</tr>
</tbody>
</table>
<hr>
<h2 id="the-reality-of-web-game-performance" tabindex="-1">The Reality of Web Game Performance <a class="header-anchor" href="#the-reality-of-web-game-performance" aria-label="Permalink to &quot;The Reality of Web Game Performance&quot;"></a></h2>
<p>No matter which engine you choose, these factors matter more:</p>
<ol>
<li><strong>Asset size</strong> — Compress textures, audio, minimize dependencies</li>
<li><strong>First scene</strong> — Make initial download small, stream the rest</li>
<li><strong>Draw calls</strong> — Batch sprites, use texture atlases</li>
<li><strong>Device testing</strong> — Test on actual mobile devices, not just desktop Chrome</li>
</ol>
<p>Start here: <a href="/tutorials/ship-web-game-fast.html">Ship a web game that loads fast</a></p>
<hr>
<h2 id="browser-compatibility-notes" tabindex="-1">Browser Compatibility Notes <a class="header-anchor" href="#browser-compatibility-notes" aria-label="Permalink to &quot;Browser Compatibility Notes&quot;"></a></h2>
<table tabindex="0">
<thead>
<tr>
<th>Feature</th>
<th>Chrome</th>
<th>Firefox</th>
<th>Safari</th>
<th>Mobile</th>
</tr>
</thead>
<tbody>
<tr>
<td>WebGL 2.0</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>WebGPU</td>
<td>✅</td>
<td>✅</td>
<td>⚠️ Partial (macOS)</td>
<td>✅ iOS 26+</td>
</tr>
<tr>
<td>SharedArrayBuffer</td>
<td>✅ (COOP/COEP)</td>
<td>✅ (COOP/COEP)</td>
<td>⚠️</td>
<td>⚠️</td>
</tr>
<tr>
<td>Wasm SIMD</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
</tbody>
</table>
<p>Safari remains the most problematic browser for advanced web game features. Always test on Safari/iOS if targeting broad audiences.</p>
<hr>
<h2 id="common-questions" tabindex="-1">Common Questions <a class="header-anchor" href="#common-questions" aria-label="Permalink to &quot;Common Questions&quot;"></a></h2>
<h3 id="what-is-the-best-game-engine-for-browser-games" tabindex="-1">What is the best game engine for browser games? <a class="header-anchor" href="#what-is-the-best-game-engine-for-browser-games" aria-label="Permalink to &quot;What is the best game engine for browser games?&quot;"></a></h3>
<p>There's no single answer because it depends on what you're building. For 2D games, Phaser and Defold are hard to beat. They're lightweight, well-documented, and produce small builds that load fast. For 3D in the browser, PlayCanvas and Three.js give you the most control without the bloat of a full desktop engine. If you need cross-platform and web is secondary, Unity or Godot can export to WebGL, but expect larger downloads. We built <a href="/engine.html">Cinevva Engine</a> on Three.js because we wanted open-source and web-first from day one.</p>
<h3 id="can-unity-export-to-the-web" tabindex="-1">Can Unity export to the web? <a class="header-anchor" href="#can-unity-export-to-the-web" aria-label="Permalink to &quot;Can Unity export to the web?&quot;"></a></h3>
<p>Yes, Unity can export WebGL builds. It works, but with trade-offs. Minimum build size is around 8 MB even for an empty project, load times are noticeably slower than web-native engines, and mobile browser support can be iffy due to memory overhead. If your game already exists in Unity and you want a web version, it's fine. If you're starting fresh and targeting web specifically, a web-native engine will give you better results.</p>
<h3 id="is-godot-good-for-web-games" tabindex="-1">Is Godot good for web games? <a class="header-anchor" href="#is-godot-good-for-web-games" aria-label="Permalink to &quot;Is Godot good for web games?&quot;"></a></h3>
<p>Godot's HTML5 export is solid and improving. The engine itself is free and open-source, which is a big draw. Build sizes start around 9 MB uncompressed (smaller with gzip), and you get proper 2D and 3D support. The main gotcha is SharedArrayBuffer, which requires specific server headers and can cause issues on some hosting setups. For indie devs who want one codebase across desktop and web, Godot is a strong pick.</p>
<h3 id="what-s-the-smallest-web-game-engine" tabindex="-1">What's the smallest web game engine? <a class="header-anchor" href="#what-s-the-smallest-web-game-engine" aria-label="Permalink to &quot;What's the smallest web game engine?&quot;"></a></h3>
<p>PixiJS comes in at roughly 200 KB, which is tiny. It's a 2D renderer though, not a full engine, so you'll be writing more code yourself. Excalibur.js is around 300 KB with more built-in game features. Phaser sits at about 500 KB and gives you a full 2D game framework. For 3D, PlayCanvas and Defold hover around 1-2 MB. The real question isn't just engine size though. It's total build size including your assets, and that's where optimization matters more than engine choice.</p>
<h3 id="which-is-the-best-game-engine-for-2026" tabindex="-1">Which is the best game engine for 2026? <a class="header-anchor" href="#which-is-the-best-game-engine-for-2026" aria-label="Permalink to &quot;Which is the best game engine for 2026?&quot;"></a></h3>
<p>It depends on what you're shipping. For web-first 3D, PlayCanvas is the strongest pick in 2026. For 2D, Defold and Phaser lead on load time and community. If you need one codebase for web and native, Godot (free) or Unity (larger builds) make sense. There's no universal &quot;best&quot; engine, only the best fit for your build size, platform, and skill level. We built <a href="/engine.html">Cinevva Engine</a> on Three.js because we wanted open-source and web-first from the start.</p>
<h3 id="what-are-the-top-2d-game-engines-for-2026" tabindex="-1">What are the top 2D game engines for 2026? <a class="header-anchor" href="#what-are-the-top-2d-game-engines-for-2026" aria-label="Permalink to &quot;What are the top 2D game engines for 2026?&quot;"></a></h3>
<p>Defold, Phaser, and Construct are the top 2D engines for web in 2026. Defold produces the smallest, fastest-loading builds. Phaser is the most popular HTML5 framework with the largest ecosystem. Construct lets you build 2D games visually with no code. For custom rendering, PixiJS and Excalibur.js give you more control at a smaller footprint.</p>
<h3 id="what-is-a-wasm-game-engine" tabindex="-1">What is a Wasm game engine? <a class="header-anchor" href="#what-is-a-wasm-game-engine" aria-label="Permalink to &quot;What is a Wasm game engine?&quot;"></a></h3>
<p>A Wasm game engine compiles your game to WebAssembly, a low-level binary format that runs at near-native speed in the browser. Godot, Defold, and Cocos Creator all use WebAssembly for their web exports, which is why they perform far better than older JavaScript-only engines. Wasm is now the standard for serious browser games in 2026.</p>
<h3 id="what-are-the-most-popular-game-engines-in-2026" tabindex="-1">What are the most popular game engines in 2026? <a class="header-anchor" href="#what-are-the-most-popular-game-engines-in-2026" aria-label="Permalink to &quot;What are the most popular game engines in 2026?&quot;"></a></h3>
<p>By install base and job demand, Unity and Unreal are the industry-standard engines in 2026, with Godot the fastest-growing open-source alternative. For games that run in the browser, Phaser is the most popular 2D framework and PlayCanvas leads web-first 3D. A bigger community means more tutorials, plugins, and answers when you get stuck, but the most popular engine isn't always the best fit for a web project, where build size and load time decide who can actually play.</p>
<h3 id="what-are-the-best-free-game-engines-in-2026" tabindex="-1">What are the best free game engines in 2026? <a class="header-anchor" href="#what-are-the-best-free-game-engines-in-2026" aria-label="Permalink to &quot;What are the best free game engines in 2026?&quot;"></a></h3>
<p>Godot is the most complete free engine for 2026. It's fully open-source under the MIT license with no revenue caps or seat fees, and it handles both 2D and 3D. For web specifically, Phaser, PixiJS, Defold, and Excalibur.js are all free and open-source too. Unity and Construct have free tiers with limits, and Unreal is free until $1M in revenue. If you want zero cost and no strings, Godot or one of the open-source JavaScript frameworks is the pick.</p>
<h3 id="what-s-the-best-game-engine-for-beginners-in-2026" tabindex="-1">What's the best game engine for beginners in 2026? <a class="header-anchor" href="#what-s-the-best-game-engine-for-beginners-in-2026" aria-label="Permalink to &quot;What's the best game engine for beginners in 2026?&quot;"></a></h3>
<p>For beginners in 2026, GDevelop and Construct are the easiest starting points because you build games visually with little or no code. If you want to learn programming alongside game design, Godot has a gentle scripting language and a huge library of free tutorials. For 2D web games specifically, Phaser is approachable once you know some JavaScript. Start with whichever matches how much code you want to write, then move up as your projects grow.</p>
<hr>
<h2 id="summary" tabindex="-1">Summary <a class="header-anchor" href="#summary" aria-label="Permalink to &quot;Summary&quot;"></a></h2>
<ul>
<li><strong>For most web games</strong>: Start with Defold, Phaser, or PlayCanvas depending on 2D vs 3D needs</li>
<li><strong>For cross-platform</strong>: Unity, Godot, or Cocos Creator, accepting larger builds</li>
<li><strong>For mini-game platforms</strong>: Cocos Creator (WeChat, TikTok, Taobao)</li>
<li><strong>For maximum control</strong>: PixiJS or Excalibur.js</li>
<li><strong>For non-programmers</strong>: Construct or GDevelop</li>
<li><strong>For high-fidelity 3D</strong>: PlayCanvas or Pixel Streaming</li>
</ul>
<p>The best engine is the one that ships your game. Start small, profile early, and optimize for load time above all else.</p>
<h2 id="related" tabindex="-1">Related <a class="header-anchor" href="#related" aria-label="Permalink to &quot;Related&quot;"></a></h2>
<ul>
<li><a href="/guides/godot-vs-unity-web-games.html">Godot vs Unity for Web Games</a> — the two big full engines, head to head on web</li>
<li><a href="/guides/unity-alternatives-web-games.html">Unity Alternatives for Web Games</a> — leaving Unity? pick a web engine by why you're switching</li>
<li><a href="/guides/best-2d-game-engines-2026.html">Best 2D Game Engines for 2026</a> — if your game is 2D, start here</li>
<li><a href="/guides/playcanvas-vs-threejs.html">PlayCanvas vs Three.js</a> — engine vs library for web 3D</li>
<li><a href="/guides/threejs-vs-babylonjs.html">Three.js vs Babylon.js</a> — the two big web 3D libraries</li>
<li><a href="/guides/construct-vs-gdevelop.html">Construct vs GDevelop</a> — the no-code 2D matchup</li>
<li><a href="/guides/web-games-stack-2026.html">Web Games Tech Stack in 2026</a> — WebGL vs WebGPU vs Wasm at the API level</li>
<li><a href="/tutorials/canvas-2d-game-loop.html">Canvas 2D game loop</a> — build a game without any engine at all</li>
<li><a href="/guides/game-jams-hackathons.html">Game Jams &amp; Hackathons</a> — choosing an engine for 48-hour jam constraints</li>
<li><a href="/tutorials/game-physics-libraries.html">Game physics libraries</a> — physics engines that plug into any of these frameworks</li>
<li><a href="/guides/itch-io-launch-guide.html">How to Launch Your Game on itch.io</a> — web game distribution after you've picked your engine</li>
</ul>
]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Web Games Tech Stack in 2026]]></title>
            <link>https://app.cinevva.com/guides/web-games-stack-2026</link>
            <guid>https://app.cinevva.com/guides/web-games-stack-2026</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A practical guide to WebGL, WebGPU, and WebAssembly for web game developers, with real code examples and decision frameworks.]]></description>
            <content:encoded><![CDATA[<h1 id="web-games-tech-stack-in-2026" tabindex="-1">Web Games Tech Stack in 2026 <a class="header-anchor" href="#web-games-tech-stack-in-2026" aria-label="Permalink to &quot;Web Games Tech Stack in 2026&quot;"></a></h1>
<p>Three technologies power web games: WebGL, WebGPU, and WebAssembly. Each solves different problems and comes with different trade-offs. This guide helps you pick based on what you're actually building, not what's getting the most hype.</p>
<h2 id="the-quick-answer" tabindex="-1">The Quick Answer <a class="header-anchor" href="#the-quick-answer" aria-label="Permalink to &quot;The Quick Answer&quot;"></a></h2>
<p>WebGL 2.0 works everywhere and handles most games just fine. Use it when you need broad compatibility, especially on mobile. WebGPU gives you compute shaders and better performance, but you'll lose users on older browsers and devices. WebAssembly makes your CPU code faster, so it's useful for physics and pathfinding, but it won't help if your bottleneck is the GPU.</p>
<p>Most games in 2026 ship WebGL with optional WebGPU for capable browsers. Wasm gets used selectively for hot code paths, not the whole game.</p>
<h2 id="webgl-2-0-the-boring-choice-that-works" tabindex="-1">WebGL 2.0: The Boring Choice That Works <a class="header-anchor" href="#webgl-2-0-the-boring-choice-that-works" aria-label="Permalink to &quot;WebGL 2.0: The Boring Choice That Works&quot;"></a></h2>
<p>WebGL 2.0 has been stable since 2017. Every modern browser supports it. Your game runs on Chrome, Firefox, Safari, and Edge going back 5+ years. It works on iOS Safari 15+, Chrome for Android, and Samsung Internet. It even runs in console browsers like Xbox Edge and PlayStation's browser.</p>
<p>Here's what a basic WebGL 2 setup looks like:</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> canvas</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> document.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getElementById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'game'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> gl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> canvas.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgl2'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">gl) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // Fallback to WebGL 1 or show error</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> gl1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> canvas.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgl'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">gl1) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    showError</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Your browser does not support WebGL.'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Now you have a GL context</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">gl.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clearColor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1.0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">gl.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clear</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(gl.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">COLOR_BUFFER_BIT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre>
</div><h3 id="what-you-get" tabindex="-1">What You Get <a class="header-anchor" href="#what-you-get" aria-label="Permalink to &quot;What You Get&quot;"></a></h3>
<p>WebGL 2 gives you instanced rendering so you can draw thousands of objects with one draw call. It has transform feedback for GPU-side particle systems and simulations. You get multiple render targets for deferred rendering and G-buffers, 3D textures for volumetric effects, and integer textures for precise data storage.</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">gl.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">drawArraysInstanced</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(gl.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">TRIANGLES</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, vertexCount, instanceCount);</span></span></code></pre>
</div><h3 id="what-you-don-t-get" tabindex="-1">What You Don't Get <a class="header-anchor" href="#what-you-don-t-get" aria-label="Permalink to &quot;What You Don't Get&quot;"></a></h3>
<p>You can't run compute shaders for general-purpose GPU compute. There's no bindless textures, so you're limited by texture unit count. No persistent mapping or explicit memory control. No mesh shaders or modern geometry pipeline.</p>
<p>For most 2D games and many 3D games, these limitations don't matter. WebGL 2 shipped some of the most successful web games ever made.</p>
<h2 id="webgpu-when-you-need-more" tabindex="-1">WebGPU: When You Need More <a class="header-anchor" href="#webgpu-when-you-need-more" aria-label="Permalink to &quot;WebGPU: When You Need More&quot;"></a></h2>
<p>WebGPU is designed around how modern GPUs actually work. Chrome shipped it in May 2023, and by late 2025 all major browsers had support. Chrome 113+, Firefox 145+, Safari 26+, and Edge 113+ all work. Chrome Android supports it on recent devices, and iOS Safari 26+ supports it too.</p>
<p>The catch is that older devices and browsers don't have it, so you need a fallback strategy.</p>
<h3 id="what-you-get-1" tabindex="-1">What You Get <a class="header-anchor" href="#what-you-get-1" aria-label="Permalink to &quot;What You Get&quot;"></a></h3>
<p>Compute shaders let you run general-purpose GPU compute for physics, particles, AI, and image processing.</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// A compute shader that processes data in parallel</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> computeShaderCode</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> `</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">@group(0) @binding(0) var&#x3C;storage, read_write> data: array&#x3C;f32>;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">@compute @workgroup_size(64)</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">fn main(@builtin(global_invocation_id) id: vec3&#x3C;u32>) {</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">  data[id.x] = data[id.x] * 2.0;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">}</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">`</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre>
</div><p>You also get explicit resource management which means fewer performance surprises, render bundles to pre-record draw calls for repeated use, and WGSL as a modern shader language designed for GPUs rather than a C-like hack.</p>
<h3 id="practical-webgpu-setup" tabindex="-1">Practical WebGPU Setup <a class="header-anchor" href="#practical-webgpu-setup" aria-label="Permalink to &quot;Practical WebGPU Setup&quot;"></a></h3>
<p>Here's how to initialize WebGPU with a WebGL fallback:</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> initGraphics</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">canvas</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // Try WebGPU first</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (navigator.gpu) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> adapter</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> navigator.gpu.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">requestAdapter</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (adapter) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> device</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> adapter.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">requestDevice</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> context</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> canvas.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgpu'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      </span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">configure</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        device,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        format: navigator.gpu.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getPreferredCanvasFormat</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      });</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgpu'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, device, context };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // Fall back to WebGL 2</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> gl</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> canvas.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgl2'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (gl) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgl2'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, gl };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">  // Last resort: WebGL 1</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> gl1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> canvas.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgl'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (gl1) {</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> { type: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'webgl'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, gl: gl1 };</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  </span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  throw</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Error</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'No graphics API available'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="when-it-actually-helps" tabindex="-1">When It Actually Helps <a class="header-anchor" href="#when-it-actually-helps" aria-label="Permalink to &quot;When It Actually Helps&quot;"></a></h3>
<p>WebGPU shines when you need compute shaders to update millions of particles without CPU round-trips, GPU-accelerated collision detection and cloth simulation, procedural generation of terrain or textures or meshes, complex post-processing effects like SSAO, bloom, and depth of field, or when you want to run trained models for NPC behavior or image effects.</p>
<p>If you're making a puzzle game or a visual novel, WebGPU won't help you. If you're making a particle-heavy action game or a complex 3D world, it might be worth the compatibility trade-off.</p>
<h2 id="webassembly-fast-cpu-code" tabindex="-1">WebAssembly: Fast CPU Code <a class="header-anchor" href="#webassembly-fast-cpu-code" aria-label="Permalink to &quot;WebAssembly: Fast CPU Code&quot;"></a></h2>
<p>WebAssembly runs compiled code at near-native speed. It's not about graphics. It's about making your CPU code faster.</p>
<h3 id="when-it-helps" tabindex="-1">When It Helps <a class="header-anchor" href="#when-it-helps" aria-label="Permalink to &quot;When It Helps&quot;"></a></h3>
<p>Wasm works well for physics engines (Box2D, Bullet, and Rapier all have Wasm builds), pathfinding on large grids, asset decompression, emulating old game consoles, and porting existing C++ or Rust codebases to the web.</p>
<h3 id="when-it-doesn-t-help" tabindex="-1">When It Doesn't Help <a class="header-anchor" href="#when-it-doesn-t-help" aria-label="Permalink to &quot;When It Doesn't Help&quot;"></a></h3>
<p>Your GPU doesn't care whether draw calls come from JavaScript or Wasm, so rendering won't be faster. I/O bound code like fetching assets or network requests won't benefit either. And if your JavaScript already runs in under a millisecond, Wasm won't save you.</p>
<h3 id="a-practical-wasm-example" tabindex="-1">A Practical Wasm Example <a class="header-anchor" href="#a-practical-wasm-example" aria-label="Permalink to &quot;A Practical Wasm Example&quot;"></a></h3>
<p>Here's a minimal Rust function compiled to Wasm for physics:</p>
<div class="language-rust vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">rust</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// src/lib.rs</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">#[no_mangle]</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">pub</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> extern</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "C"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> step_physics</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dt</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> f32</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">    // Your physics code here</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><p>Compile with:</p>
<div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wasm-pack</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --target</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> web</span></span></code></pre>
</div><p>Use in JavaScript:</p>
<div class="language-javascript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">javascript</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> init, { step_physics } </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './physics_bg.wasm'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> init</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> gameLoop</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">dt</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  step_physics</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(dt); </span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">// Runs at near-native speed</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  render</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  requestAnimationFrame</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(gameLoop);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre>
</div><h3 id="threading-gets-complicated" tabindex="-1">Threading Gets Complicated <a class="header-anchor" href="#threading-gets-complicated" aria-label="Permalink to &quot;Threading Gets Complicated&quot;"></a></h3>
<p>Wasm can use threads for parallel processing, but it requires <code>SharedArrayBuffer</code>, which means you need cross-origin isolation headers on your server:</p>
<div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0" v-pre=""><code><span class="line"><span>Cross-Origin-Opener-Policy: same-origin</span></span>
<span class="line"><span>Cross-Origin-Embedder-Policy: require-corp</span></span></code></pre>
</div><p>These headers break things. Third-party iframes without CORP headers stop working, some analytics scripts break, and OAuth popups can fail. You can use <code>credentialless</code> instead of <code>require-corp</code> to reduce the damage, but it's still messy.</p>
<p>If you can't set these headers because you're on shared hosting or itch.io, you can't use Wasm threads. Single-threaded Wasm still works fine though.</p>
<h2 id="real-decisions-for-real-games" tabindex="-1">Real Decisions for Real Games <a class="header-anchor" href="#real-decisions-for-real-games" aria-label="Permalink to &quot;Real Decisions for Real Games&quot;"></a></h2>
<p>If you're making a 2D platformer, use WebGL 2 through something like Phaser or PixiJS. Skip WebGPU because it's overkill and skip Wasm because JavaScript is fast enough for 2D physics. Broad compatibility matters more than the latest features, and your bottleneck is content, not technology.</p>
<p>If you're making a 3D open world, start with WebGL 2 but design for a WebGPU upgrade path. Consider Wasm for physics using Rapier or Bullet. You want the broadest reach now, but compute shaders would help with foliage, particles, and LOD later. Physics in Wasm keeps the CPU budget low.</p>
<p>If you're porting a C++ engine, use Wasm through Emscripten. Graphics will be WebGL 2 by default, or WebGPU if your engine supports it. You already have the code and Emscripten handles the translation.</p>
<p>If you're making a puzzle game, use Canvas 2D or WebGL 2 through Phaser. Skip everything else. Simple games should stay simple.</p>
<p>If you absolutely need maximum performance and you're willing to lose some users on older browsers, go with WebGPU plus Wasm. Just measure the actual impact on your audience before committing.</p>
<h2 id="what-actually-makes-games-fast" tabindex="-1">What Actually Makes Games Fast <a class="header-anchor" href="#what-actually-makes-games-fast" aria-label="Permalink to &quot;What Actually Makes Games Fast&quot;"></a></h2>
<p>Here's what determines if your web game runs well, in order of importance.</p>
<p>Asset size accounts for about half of perceived performance. A 2MB game that loads in 1 second feels faster than a 50MB game with better FPS. Compress everything. Lazy-load what you can.</p>
<p>Draw calls matter a lot for 3D games, maybe 30% of your performance budget. Batch your geometry. Use texture atlases. Instance repeated objects. This matters way more than WebGL vs WebGPU.</p>
<p>JavaScript performance is maybe 15%. Avoid allocation in hot loops. Use typed arrays. Profile before you optimize.</p>
<p>Graphics API choice? Honestly, maybe 5%. For most games, the API matters less than how you use it.</p>
<p>If your game is slow, check whether you're loading too much upfront. Then check if you're issuing too many draw calls. Then check if your JavaScript is doing something dumb in the game loop. Only after all that should you ask whether a different graphics API would help.</p>
<h2 id="what-i-d-actually-use" tabindex="-1">What I'd Actually Use <a class="header-anchor" href="#what-i-d-actually-use" aria-label="Permalink to &quot;What I'd Actually Use&quot;"></a></h2>
<p>For a new web game starting today, I'd use Three.js or Babylon.js for rendering since they abstract away WebGL and WebGPU. For physics, Rapier (Rust compiled to Wasm) if I need 3D physics, or just the engine's built-in 2D physics for simpler games. Howler.js or the Web Audio API directly for audio. Vite for building because it's fast in dev and produces good production builds. And static hosting on Netlify, Vercel, GitHub Pages, or itch.io.</p>
<p>This stack ships games that work on 98%+ of devices while being ready for WebGPU when it becomes the default.</p>
<h2 id="test-before-you-commit" tabindex="-1">Test Before You Commit <a class="header-anchor" href="#test-before-you-commit" aria-label="Permalink to &quot;Test Before You Commit&quot;"></a></h2>
<p>Before you lock in a tech stack, build a tiny prototype and actually test it. Check load time on 3G using Chrome DevTools throttling. Your game should be playable in under 5 seconds on a slow connection. Test performance on a low-end Android phone, either borrow one or use BrowserStack. If it runs there, it runs everywhere. Test on Safari specifically because it's different enough to cause surprises. And if your game will be on Newgrounds or Kongregate, test it in an iframe.</p>
<p>These tests catch more real problems than debating WebGL vs WebGPU ever will.</p>
<h2 id="more-reading" tabindex="-1">More Reading <a class="header-anchor" href="#more-reading" aria-label="Permalink to &quot;More Reading&quot;"></a></h2>
<p><a href="/guides/web-game-engines-comparison.html">Web Game Engines Comparison</a> covers full engines if you don't want to build from scratch. <a href="/guides/threejs-usdc-tech-report.html">Three.js + USDC in the Browser</a> shows how to load USD assets in Three.js. <a href="/guides/itch-io-launch-guide.html">How to Launch on itch.io</a> covers publishing once you've built something.</p>
<p>For hands-on tutorials that go deeper into each API:</p>
<ul>
<li><a href="/tutorials/webgl-fundamentals-for-games.html">WebGL fundamentals for game developers</a> — shaders, buffers, textures, and the render loop</li>
<li><a href="/tutorials/webgpu-getting-started.html">WebGPU getting started</a> — device setup, pipelines, and WGSL shaders</li>
<li><a href="/tutorials/web-workers-game-logic.html">Web Workers for game logic</a> — offloading work to background threads</li>
<li><a href="/tutorials/coop-coep-sharedarraybuffer.html">COOP/COEP and SharedArrayBuffer</a> — headers required for Wasm threading</li>
<li><a href="/tutorials/game-physics-libraries.html">Game physics libraries</a> — Rapier, Cannon-es, Ammo.js, Matter.js, and more</li>
</ul>
<p>The right tech stack is the one that ships your game. Pick what you know, test early, and optimize later.</p>
]]></content:encoded>
        </item>
    </channel>
</rss>