Full video: https://youtu.be/phbaxNPJxss
Full course: https://simondev.io/lessons/gamedev/
Funemployed ex-(Google/game dev). I code things, mostly game related, sometimes not.
Full video: https://youtu.be/phbaxNPJxss
Full course: https://simondev.io/lessons/gamedev/
Once you've made it through all these steps
• Reuse materials
• Batch/instance
• Optimize data
• Cull
• LOD/imposters
We're hitting 1 million+ trees, for very little CPU/GPU cost.
Octahedral imposters are a powerful technique, where we render the object from many angles into an atlas texture, and then just show a billboard in the world.
The key detail, it responds to camera movement and lighting, but it's just smoke and mirrors.
TSL makes it easy to hook into the lighting system, making it seemless.
At this point, the last lever left is reducing quality, but it’s a powerful one.
LOD (level-of-detail) works by dropping detail with distance. As an object gets further away, you swap meshes (LOD0 -> LOD1 -> LOD2) and nobody notices (hopefully)
With instancing, you'll have to do this manually with an InstancedMesh for each level.
At some point it’s hard to “draw faster”. So stop drawing stuff you can’t see.
Frustum culling removes anything offscreen.
This isn't automatic with InstancedMesh, so you can either:
• Instance within a chunk, then cull by chunk.
• Cull manually per-instance
We’re at 250k+ trees now.
(sidenote: occlusion culling, scene-dependent but huge when it applies)
You can quantize way further than most people think.
It’s possible to squeeze a ~56B vertex down to ~16B with packing + quantization.
TSL makes unpacking clean (override attributes via node API).
Source: https://x.com/SebAaltonen/status/1515735247928930311
This gets us to 50k+ trees.
Now take a look at your data.
You want GPU-friendly assets, not just smaller downloads.
Meshes: weld verts, simplify, quantize
Textures: Use GPU compressed formats (like ETC1S/ UASTC)
I use my in-browser GLB optimizer to do most of this: https://gltf-optimizer.simondev.io
Instancing allows us to tell the GPU in a single draw call: "hey, draw this thing a zillion times".
No need for the CPU to constantly submit draw commands, which alleviates the load on the CPU and shifts the bottleneck to the GPU.
We're hitting 30k+ trees now.
To draw a lot of stuff, you want to reduce materials. Collapse different materials into a single material by packing textures into atlases.
Then you can collapse draw calls with:
• InstancedMesh (same geo)
• BatchedMesh (different geo)
Low-hanging fruit: stop duplicating assets.
Share geometry & materials across instances and you'll see immediate improvements in the framerate.
This change alone gets us to ~700–800 trees at 60fps.
Draw calls are often the first hurdle.
In this example, as the trees stream in, the FPS drops, and we cap out around ~500 or so.
The first step is to make sure you’re measuring the right things. You need both CPU time and GPU time, to understand where the problems lie.
I use three-perf or the Three.js Inspector so I can see both numbers easily.
Optimization can be tricky.
Here’s how to go from drawing a few hundred trees to virtually unlimited in Three.js, step by step.
This will be high level, but not so much that you can’t fill in the details.
If you're building a 3D website or a game, you really want your assets compressed this way.
Free, desktop-only, KTX2 + mipmaps, mesh compression options (including Draco), built on the already excellent gltf-transform by
@donrmccurdy
Link again: https://gltf-optimizer.simondev.io
If you hit issues, DM me with browser + sample file.
Most online tools only optimize file size, but you still have to reinflate them in memory, and do extra work to decompress them on load.
One Sketchfab example:
No compression: 39 MB file, 342 MB in memory
Other online tools: 4.26 MB file, 22 MB in memory
gltf-optimizer: 0.68 MB file, 6 MB in memory
I built this little tool to optimize and re-export GLTF/GLB assets to GPU-compressed KTX2. It runs entirely client-side/in-browser.
You can fiddle with the parameters, compare the before/after, and export to GLB.
Try it here: https://gltf-optimizer.simondev.io
Dusted off my octahedral imposter demo for someone. Sprinkled in various trees from http://quaternius.com/
Should make them react to lighting properly and all that.
🔔 TSL exercises now available!
TSL is the future of Three.js, unlocking the power and performance of WebGPU.
The integrated shader homework on my site now includes experimental TSL support. This is a free upgrade for all existing Shader students.
Check out the site:
🔔 Site update: the new SimonDev site is already super functional and getting better every day.
Latest addition: integrated shader homework where you edit GLSL in-browser and match the target output. It’s a polished evolution of my 2023 GLSL site.
As a reminder, anyone who bought the course on Teachable can access the same content (and much more) on my new site.
Link below 👇