Synthwave Terrain
A magenta wireframe grid rolls with hills generated from a hand-written, dependency-free layered-noise function, endlessly scrolling toward the camera — not by moving the mesh, but by advancing the coordinate the noise is sampled at every frame and rewriting the geometry's position attribute in place. A gradient sun disc, sliced by a handful of horizontal cutouts, glows on the horizon above a fogged sky scattered with faint stars.
How it works
- Deterministic layered noise, no library:
hash(x, y)is a pureMath.sin-based hash over two integers (noMath.random, so it's reproducible),valueNoisebilinearly interpolates it across the unit grid, andfractalNoisesums three octaves of that at doubling frequency/halving amplitude — the standard fBm recipe, written out by hand instead of pulled from a noise package. - Scrolling by re-sampling, not moving: the plane's transform never changes. Every
onFrameadvancesscrollOffsetand adds it to each vertex's local-depth coordinate before sampling the noise, so the pattern slides under the fixed mesh — no seams, no reset, no per-frame geometry reallocation. See Animation & Loop. primitivefor imperative geometry: per-vertex position rewriting every frame isn't expressible through declarativeargs/props, so the animatedTHREE.Meshis built up front and adopted via{ primitive: null, object: terrain }— the same recipewave-field.tsuses for its per-instance matrix rewrites. Seeprimitive.needsUpdatewithoutcomputeVertexNormals: the material iswireframe: trueand unlit (MeshBasicMaterial), so it never reads vertex normals — onlypositionAttribute.needsUpdate = trueis set after the rewrite, skipping the normal recompute a lit/shaded mesh would need.- Rotation maps local axes to world axes: the plane is built flat (
PlaneGeometry's local X/Y/Z), thenrotation.x = -Math.PI / 2on the mesh maps local Z (the axis the noise writes height into) to world Y (up) and local Y to world -Z (depth) — that's whyterrainHeight's second argument reads as "distance from camera" rather than a raw local coordinate. - The sun is a
CanvasTexture, not a shader:circleGeometrygets ameshBasicMaterialwhosemapis a 2D canvas — a vertical multi-stop gradient with a handful of growing horizontal bands punched out viaglobalCompositeOperation = "destination-out", the classic "sliced sun" silhouette, built the same waystarfield-hero.tsbuilds its glow sprite. - Selective
fog: false: the scene fog ({ fog: null, args: [...] },.isFog-inferred attach) fades the terrain into the backdrop as designed, but the sun and star materials setfog: false— a light source above the haze and stars in open sky shouldn't dim with ground-level atmospheric distance the way the grid does. See Attach inference. frameloop: "always": the scroll is a free-running per-tick animation with nothing that ever callsinvalidate(), so"demand"mode would render exactly one frame and freeze — see frameloop modes.