NaturalEcosystems is an interactive web app that lets you explore eight natural ecosystems — from jungle canopies to the ocean floor to drifting icebergs — as navigable 3D (and two flat 2D "diorama card") scenes, each with a short fact and description. It's built as a plain Node.js/Express backend serving a REST API and a set of procedurally generated glTF models, paired with a React (Vite) + React Three Fiber frontend.

Inspiration

Most educational content about ecosystems is either a wall of text or a static photo gallery. We wanted something you could actually look around inside — orbit a jungle canopy, zoom into coral, watch a lizard sit next to a cactus — while still keeping each scene anchored to a real, concise fact rather than just being a tech demo.

What it does

The app presents eight ecosystem scenes, each with its own background gradient, title, short description, and standout fact:

6 "3D" scenes you can freely orbit: Tropical Forest, Ocean Floor, Cloud Forest, Seagrass Meadow, Tropical Savanna, Desert Fauna. 2 "2D" scenes presented as flat, front-on illustrated cards with rotation disabled: Epiphytic Canopy Ecosystem, Large Icebergs Drift.

Every scene loads a real .glb (binary glTF) model over HTTP from the Express backend, and the ecosystem metadata (title, description, fact, background colors) comes from a REST API rather than being bundled into the frontend build.

How we built it Backend: Express serves GET /api/ecosystems and GET /api/ecosystems/:slug, plus the static .glb files. Frontend: a Vite + React SPA fetches the ecosystem list at runtime and renders it with React Three Fiber, using @react-three/drei's useGLTF for model loading and OrbitControls for navigation. 3D vs 2D, as a real implementation difference, not just camera angle: 3D scenes get OrbitControls with rotation enabled and a soft Environment preset for reflections; 2D scenes explicitly set enableRotate={false} and lock the camera front-on, and their underlying geometry is authored as thin, single-depth-plane shapes with nothing meaningful to see from the side. Models: all 8 .glb files are procedurally generated with Python (trimesh, plus shapely + mapbox-earcut for the irregular polygon-extruded iceberg shapes), using per-vertex color gradients instead of flat single colors, and multiple layered elements per scene (coral branches, swimming fish, cactus flowers, elephant silhouettes, hanging vines) for visual richness. Deployment: a vercel.json splits the app into a serverless function (backend + models via includeFiles) and a static build (frontend). Challenges we ran into Vercel's default function bundler only traces require() calls — since the .glb files are served via express.static rather than required in code, they were silently missing from the deployed function until we added an explicit includeFiles override in vercel.json. Polygon extrusion needs a triangulation engine — building the irregular iceberg shapes with trimesh.creation.extrude_polygon failed outright with No available triangulation engine! until we installed mapbox-earcut. SSR vs. pure SPA tradeoffs — an earlier version of this project was built in Next.js, which required a dynamic(..., { ssr: false }) workaround to keep the WebGL canvas from breaking server-side rendering. Rebuilding on a plain Vite SPA removed that whole class of problem, at the cost of losing Next.js's built-in routing/SSR conveniences. A silently dropped .gitignore — our first packaging attempt using zip -r didn't include the top-level dotfile at all; caught by explicitly listing the archive contents rather than assuming the zip step worked. Accomplishments that we're proud of

Getting a real, end-to-end verified pipeline working: Python-generated .glb files that reload correctly with trimesh.load(), a real Express API tested with curl (including a correct 404 case), and a real npm run build that passes TypeScript's tsc --noEmit check before producing a working Vite production bundle — all confirmed by actually running each step, not just writing code that looks right.

What we learned Per-vertex color gradients make an enormous visual difference over flat single-color primitives, even on very simple low-poly geometry. Keeping ecosystem data server-side and API-driven (instead of bundled into the frontend) made the Next.js → plain Node.js/React rewrite much easier than it would have been with tightly coupled data imports. Deploying a persistent-feeling app to a serverless platform (Vercel) means explicitly thinking about which static assets need CDN-style hosting versus which need to travel inside the function bundle. What's next for NaturalEcosystems Add the two remaining ecosystems from the original ten-item list (Volcanic Caldera Ecosystem, Tepui Summit Ecosystem) — the data model is fully data-driven, so this is one generator function plus one API entry per ecosystem. Replace the procedurally generated geometry with real sourced/licensed 3D models for a more photorealistic look. Add a fact-checking pass against live sources for every description, since the current text is original paraphrased summary content, not independently verified citations. Real Vercel deployment verification (this build's vercel.json has only been schema-validated, not tested against a live deploy).

Built With

Share this project:

Updates

Submission history