Aquascape
I fell down the planted-aquarium rabbit hole, went looking for software to plan a build, and found the tools all did half the job — either layout or a stocking calculator, never both, and none of them let you actually see the thing alive. So I built one. It's called Aquascape, it's free and MIT-licensed, and it runs in the browser and on the desktop: github.com/joebarbere/aquascape.
It started as "I want to plan my tank." It ended as a physics-simulating, fish-schooling, algae-growing little world. Here's the story of what got built.
One document, two dimensions
The heart of it is a single .aqua document and two views of it. You design in a precise 2D editor (an HTML canvas) and flip to a real-time 3D view (Three.js) that reads the same document — every change you make in 2D shows up in 3D immediately. Same coordinates, one source of truth, two renderers.
Designing a scape
The editor is a proper design tool. You start from one of 36 real tank presets — ADA Cube Garden, UNS, Waterbox, US standard sizes — or punch in custom dimensions. Then you build:
- Substrate in multiple regions with a profile-curve editor, so you can sculpt slopes from a deep back to a shallow front.
- Hardscape — 19 real stones and woods (Seiryu, Ohko Dragon, Spiderwood, Manzanita…) — and 28 plants organized by carpet / midground / background.
- Decorations — the classic ornaments (sunken galleon, diver helmet, moai head), each a real procedurally-baked 3D model.
Editing has the niceties you'd want: drag to move, handles to scale and rotate, marquee multi-select, snap-to-everything with alignment guides, a layers panel, and composition overlays — golden ratio and rule-of-thirds guides, because aquascaping is genuinely a compositional art.
The fun part: a growth slider. Scrub from week 0 to week 52 and watch the plants fill in toward their mature size. It's deterministic — a seed in the document means the same scene grows the same way every time.
Planning the stock
Design is only half of it. Aquascape also plans livestock and equipment — 24 species (tetras, rasboras, gouramis, that one angelfish that terrorizes everyone, shrimp, snails) and 18 pieces of equipment including nine real LED lights with their manufacturer-published lumen and color-temperature specs.
And it checks your work. A rules engine surfaces explainable stocking warnings: bioload vs. tank volume, temperature and pH range mismatches, peaceful-meets-aggressive temperament clashes, schooling fish stocked below their minimum, and fin-nippers sharing a tank with long-finned targets. No black-box "you're at 87% capacity" — every warning tells you why.
The 3D tank is actually alive
This is where the project got gloriously out of hand. The 3D view isn't a static render — it's a simulation.
The tank itself looks the part: physically-based transmissive glass with real refraction, filmic tone mapping, soft shadows, procedural underwater caustics rippling across the substrate, an animated water surface, and a full day-night cycle you can scrub. Plants sway — and sway wider and faster near a filter outflow, because the sway is coupled to a real flow field.
But the fish are the thing. Every livestock entry becomes a real, behaving fish, simulated by an entity-component system at a fixed 30 Hz:
- Schooling via the Couzin three-zone model — tetras shoal, loners wander.
- Vertical stratification — hatchetfish hug the surface, cories scoot along the bottom, shrimp and snails crawl.
- Territoriality — cichlids claim hardscape caves and chase intruders.
- Fin-nipping — tiger barbs harass slow, long-finned tankmates (and behave when their own school is big enough, just like the real ones).
- Fear and startle waves that ripple through a school when the angelfish gets too close.
And underneath all of it runs a live nitrogen cycle. Feeding adds bioload, waste raises ammonia, a two-stage nitrification model converts ammonia → nitrite → nitrate with a bacterial colony that grows over time, and fish health actually responds to the water quality. There's a working test-kit readout (the classic API color chart) and water changes that dilute the water column — but honestly, so a water change lowers nitrate without resetting your cycle. Even the algae is simulated: four types growing per-surface driven by nitrate × photoperiod × flow, and the right cleanup crew eats the right type — otos take diatoms, plecos take green film, an SAE goes after black beard.
The whole simulation is deterministic and budgeted: the same seed replays byte-identical, and 200 fish step in about 3.4 ms. There's also a fish-eye view — park the camera at a fish's eye and ride it through the tank in first person.
…and then it became a game
Once you have a living tank, you may as well play with it. Aquascape has a showcase mode that opens borderless and fullscreen into a pre-built show tank with 100+ schooling fish, plus a Quake-style developer console (hit ~) that drives everything by command — fish add cardinal 24, water 540, light dusk, dose easy-green — with history and tab-completion, and you can save and reload your own demo scenes.
And there are four genuinely playable mini-games where you are a fish: hunt prey as a predator, outlast the hunters in survival, eat falling food without gorging in feeding, or scrub algae and siphon waste against the clock in cleaner. Each has objectives, a score, and a real health/hunger HUD.
Save, share, ship
Your design lives in a lossless .aqua file — a versioned format with a migration chain, so files from an older build always open. There's crash-recovery autosave, image export up to 4K, and a setup-sheet export that turns your design into a Markdown/JSON spec sheet: dimensions, water volume, every plant and stone, the full livestock and equipment list, and the stocking warnings — basically a shopping list for the real build.
It ships two ways: an installable web PWA that works offline, and an Electron desktop app for macOS, Windows, and Linux, all from one Nx monorepo.
Under the hood
A few architectural bets kept a project this sprawling from collapsing:
- One scene model, two renderers. The domain logic is framework-free; the 2D canvas and the 3D Three.js views both implement the same renderer interface over the same millimeter coordinates.
- Every mutation is a Command with apply/invert — so undo/redo, persistence, and the live simulation all build on one primitive, and the UI never touches the scene directly.
- One feature codebase, two apps — features depend on a
platform-apiinterface, and the web and desktop builds inject their own implementations. - Layering is mechanical — Nx module boundaries make an illegal import fail the linter, so the architecture can't quietly rot.
I built the whole thing with Claude Code as my pair — 186 commits over about three weeks — with a test suite that includes a property-based document round-trip check and a Playwright end-to-end test that literally asserts a fish paints in the 3D view before a PR can merge.
Wrapping up
What began as "let me plan my little planted tank" turned into an open-source studio with a compositional design surface, a deterministic growth model, an explainable stocking engine, and a 3D world with schooling fish, real water chemistry, growing algae, and mini-games you can play from inside a cardinal tetra. It's MIT-licensed and out in the open — take it for a swim.
AI icon by Flaticon.