3D Alpine Forest
A small experiment in procedural 3D, scroll-driven storytelling and building visual work with AI.
The result is a single continuous journey through an alpine valley at dusk. As you scroll, the camera follows a river through six chapters, gradually moving towards the setting sun.
The whole scene is built with Three.js, and everything you see is created in code. There are no external 3D models, textures, images or environment assets.
Building the landscape from scratch
The terrain, river, trees, rocks, grass, flowers, atmospheric particles, sun and stars are all generated when the scene loads.
Even the flower texture is created dynamically in the browser. A small canvas draws the sprite using colours from the scene's palette, which means it always stays visually consistent. Details such as rock and bark patterns are generated directly in shaders rather than loaded as image textures.
Three.js provides the foundation for the 3D scene, while the project uses a regular build process and is deployed through GitHub and Netlify. The important constraint was not avoiding tooling, but avoiding pre-made visual assets. The landscape itself, its materials and all of its visual details are generated from code.
A single height field forms the foundation of the landscape. It determines the terrain geometry, where trees and rocks can be placed, and how the water interacts with the surrounding ground. The riverbank is therefore not manually drawn. It naturally appears wherever the terrain meets the water level, which gives the river its irregular path through the valley.
Lighting works a little differently too. There are no traditional lights in the Three.js scene. Instead, the visual style is built around flat colours, depth layers and atmospheric haze. Objects gradually blend towards the colour of the sky as they move into the distance.
The sun is the main visual reference point, with the river acting as the brightest accent in the composition.
Some parts of the scene are more literal than they might appear. The position of the sun is calculated using real astronomical data for 46.5° north. The orientation of the valley was adjusted so that the composition matches the position of the sun at 19:46 on 21 June. The night sky also contains 1,630 real stars down to magnitude 5.0.
Building something visual with AI
A large part of the experiment was figuring out how to work on a visual scene together with an AI that cannot reliably judge the final frame itself.
That changed the way we worked.
Instead of relying only on statements like "this looks closer", we tried to turn as many visual decisions as possible into things that could be measured and compared.
Reference images were divided into masks for different areas of the composition. Interestingly, manually creating those masks worked better than the automatic segmentation approaches we tested.
Headless renders could then be compared with the reference using measurements such as average colour difference, silhouette shape and luminance within specific regions.
We also built a simple test that moves the sun across the sky and measures how much foliage receives no shading in each chapter. This helped check whether a scene designed around one specific time of day would still behave properly as the lighting changed.
None of this replaces looking at the result. It simply makes the iterations more useful.
Visual judgement still happens at the end, but by that point the scene already arrives in a predictable state. A single URL can define the chapter, time of day and any settings that differ from the final version, making it easy to reproduce exactly what is being reviewed.
Keeping millions of triangles under control
Performance became another part of the experiment.
Grass accounts for roughly 95% of the scene's geometry, with several million triangles visible across the landscape. That means even small changes to density or draw distance can have a surprisingly large impact.
Before adding more instances to the scene, we started estimating their cost first. In a procedural environment, a seemingly small decision multiplied across an entire valley can quickly become the most expensive thing on screen.
That constraint ended up influencing the visual design just as much as the code.