Inspiration

Two problems, one metaphor.

For debugging: Debuggers tell the truth in the least memorable format possible : a stack trace, a red line, a variable inspector full of hex addresses. Every programmer has had the experience of finally understanding a bug and feeling like the shape of the problem was almost physical, almost visible, if only the tools showed it that way.

For learning: Recursion, scope, and memory, I learnt them with static diagrams that don't move. Students memorize the definition of a stack frame I also did that without ever developing an intuition for what recursion feels like as a process a kind of descent, the branching, the unwinding. An array bounds check is a rule to follow, not a shape to recognize.

Plants already have a vocabulary for both of these problems. I used that to make this.

That mapping felt too good not to try: what if a running program was a plant, and its bugs weren't logged, but grown and its structure wasn't hidden behind text, but drawn?

What it does

Halting Garden runs a small custom language, Sprout🌱, through a tree-walking interpreter and turns the execution trace into a live, animated botanical structure in the browser. Turning the program into a plants biology.

The structure is the point. You can watch recursion fork (because each call extends a branch), and iterate (because a loop grows around, not outward). You can see scope entry and exit as leaves appearing and vanishing. You see the shape of the algorithm in the shape of the plant, not by reading the code and simulating it in your head.

When something goes wrong - that's where it becomes a tool, not just a visualization.

Clicking anything in the garden a branch, a leaf, a root - jumps you to the corresponding source line and the inspector showing the live call stack and variable state at that point. Debugging becomes bidirectional: code to plant and plant to code.

How I built it

The pipeline is: Sprout source → lexer → recursive-descent parser → AST → tree-walking interpreter, running inside a Web Worker so a program the user writes (including a deliberately infinite loop, which several of the presets are) can never lock up the main thread. The interpreter doesn't mutate any shared UI state directly, it simply emits a stream of typed events (call_enter, loop_enter, alloc, free, leak_detected, error, and so on) across the worker boundary.

Those events are consumed by a pure reducer that folds them into an immutable GrowthNode tree — the actual "plant" data structure, with parent/child links, per-node disease level, and a list of roots and leaves attached to each node. A stateless Canvas 2D renderer that harbors our garden.

Keeping the interpreter, the reducer, and the renderer strictly decoupled interpreter emits events, reducer builds a tree, renderer only ever reads that tree - turned out to be the single most load-bearing architectural decision in the project. It's what made click-to-code, replaying execution up to any point in time, and the animated disease-spread all possible without the renderer needing to understand language semantics at all.

Challenges I faced

Language design from scratch. Building even a small language like Sprout requires decisions at every level - tokenization, parsing, scope rules, memory model. Early on I had planned multi-language support (JavaScript and Python transpilers), thinking it would expand reach. But each transpiler was a rabbit hole: matching line numbers, handling variable scoping differences, dealing with implicit type coercion.

Canvas rendering that tracks a program. Rendering a plant that reflects execution is trickier - the canvas transforms (translate, rotate, scale) need to compose correctly for a coordinate system that tracks depth, branches, and camera zoom simultaneously.

Event explosion in dense recursion. Fibonacci and other branching recursion generate exponential call stacks, which means exponential events. A naive renderer that processed every event could handle fib(6) but choked on fib(10). The fix: batch leaf clustering (merging distant leaves into billboarded clusters).

Accomplishments that I'm proud of

Sprout : Though having limited functionality, it is the backbone of this whole project and makes it possible for the application to be simple and accessible .

Illustrative learning : When I learnt these same concepts they were a pain to grasp and more so to visualize this project aims to support that journey in a simplistic way.

Usefulness : This project though in currently a prototype, it can be enhanced on a better scale to be helpful in various ways.

UI : The UI has turned out great and interactive.

What I learned

The biggest lesson was about verification discipline. You can pass all your tests and still have broken work. Midway through the renderer, a test suite reported "all geometry correct" while the in the running application the plant rendering impossibly small a direct contradiction. The tests weren't wrong; they were testing nothing.

The second lesson was about scope discipline. I wanted to add many things to this project, many that didn't even made sense to put in but I wanted to buff it with features, but I realized that having all that will hinder its capability of being a simple tool, would require some thought behind everything. Would be cautious in future project not to overshoot.

What's next for Halting Garden

This project will be expanded to support multiple languages so that it can help in various ways. New features to support diverse use cases can be achieved with open-sourcing this project and letting others contribute to its future.

Built With

Share this project:

Updates

Submission history