LOG 02 · RENDERING
WebGL & Three.js
Hand-written GLSL shaders and Three.js scenes for custom 3D web experiences — nebulas, starfields, and real-time lighting, built from scratch rather than assembled from a template.
Shaders, not textures
The nebula backdrop behind this globe isn't a texture — it's a fragment shader running fractal Brownian motion (fbm) noise in real time, layered with a second noise pass to blend two color palettes across the sphere. Stars aren't sprites either: each is a GPU point rendered with per-vertex phase offsets so they twinkle independently, at a fraction of the draw calls a sprite-based approach would cost.
Writing shaders directly means the exact look — color, motion speed, glow falloff — is tunable to the value, not limited to whatever a pre-built particle library happens to expose as an option.
Real assets, real lighting
3D models load as compressed GLTF/DRACO assets, decoded client-side, with a fallback primitive swapped in until the real model finishes streaming — so the scene is never empty while a multi-megabyte asset loads on a slow connection. Materials that arrive without their own texture map get a standard material with tuned roughness/metalness/emissive values, rather than rendering flat and unlit.
Lighting is a small deliberate rig — one key directional light, one dim rim light from the opposite side — rather than relying on ambient light alone, which is what gives the globe actual form instead of looking like a flat green circle.
One camera, informed decisions
Every WebGL scene I build shares a single PerspectiveCamera across all rendered content, which matters more once a scene has multiple moving parts (see the CSS3D log entry) — one source of truth for position and field of view keeps everything in the scene, DOM or WebGL, in true perspective agreement.