Full-stack developer & designer
Hi, I’m Emmanuel.
I build web applications, simulations, and maps.
Here are a few projects and notes on how I made them.
Projects
Nov 2024
Voxel Meadow
Procedural 3D terrain generated from a seed.
Click tiles to create waves; drag to rotate.
Three.js · WebGL · GLSL
Writing
Project notes
Mapbox GL · GeoJSON · Machine Learning · GIS
The project separates model development from map rendering. The research pipeline builds a 10km grid in California Albers coordinates, clips it to the state boundary, and exports polygons in longitude and latitude for Mapbox. Training rows combine ignition records with daily weather, rolling drought proxies, terrain, and land-cover fractions.
The research began with FPA-FOD ignition records and later added WFIGS incident labels, including separate experiments for fires of at least 10 and 100 acres. Histogram Gradient Boosted classifiers are evaluated alongside XGBoost and a historical cell/month baseline. Temporal holdouts test later dates; spatial holdouts test how well the models transfer to withheld areas. Ignition-history features use past discovery dates to avoid leaking future events into training.
The monthly export pipeline aggregates daily model scores into a maximum score for each cell and month. The site loads six precomputed GeoJSON files for January–June 2026; it does not run inference in the browser. Each polygon includes a score, percentile, and risk band. Mapbox renders the polygons, while React controls month selection, playback, layer opacity, and cell inspection.
The five map colors represent percentile bands, not five absolute probabilities. The research log tracks model comparisons and feature experiments, including road access and drought data. The shipped files do not identify their model version, so they cannot be attributed to the latest experiment from the log alone.
Three.js · React Three Fiber · WebGL · GLSL
The scene uses Three.js through React Three Fiber. It builds a 10×10 tile grid on larger screens and an 8×8 grid on smaller screens. Ground, grass, and water use instanced meshes, allowing many copies of the same geometry to share a draw call. Grass blades are crossed planes, with up to 300 instances allocated per tile.
Terrain height comes from a combination of sine and cosine functions. A separate curved-river function decides which tiles contain water. The first three characters of the seed control the river’s phases, width variation, and amplitude. Grass placement, height, rotation, and color use random values, so a seed repeats the river layout rather than reproducing every blade of grass.
Custom GLSL shaders animate the materials. Grass vertices sway more near their tips, and the fragment shader varies their color from base to tip. Water combines several sine waves with two moving samples of a normal-map texture to vary its lighting and surface detail.
Clicking a ground tile starts an outward-moving pulse. Each frame, the code compares tile distance with the pulse radius and moves tile heights toward their targets; grass positions follow the ground. The implementation tracks one active wave. Dragging changes the camera’s target rotation, and its current rotation interpolates toward that target each frame at a fixed camera distance.
Canvas 2D · Particle Physics · Fluid Simulation
The effect runs on a Canvas 2D surface without an external physics engine. A fixed pool of 150 particles is reused as particles expire. Pointer movement determines where particles spawn and contributes to their launch velocity. A requestAnimationFrame loop updates position, velocity, and remaining life before drawing the next frame.
Each element changes the force rules. Water receives gravity, small oscillations, and attraction toward nearby droplets. Air uses upward drift, changing wind forces, and separation from neighbors. Fire uses stronger upward acceleration, flicker, shorter lifetimes, and weak horizontal alignment with nearby particles.
Earth spawns particles in groups with a shared chunk ID. Cohesion keeps a group together while it travels under gravity and drag. Boundary impacts break the group into fragments that inherit its motion. Unlike the other elements, earth particles are not attracted to the pointer; they receive a small repulsion when it gets too close.
The renderer accumulates particle influence into a coarse density grid. Each particle contributes to nearby cells, weighted by distance and remaining life. The canvas draws small squares with size and color derived from that field, making nearby particles appear as connected blobs rather than separate dots.
About
I’m a full-stack developer and designer. My work includes web applications, data visualization, and real-time graphics.
TypeScript / React / Python / PostgreSQL / Three.js / GLSL