Interactive 3D Arduino Simulator
February 3, 2026
Browser-based Arduino simulator where you write real Arduino C++ code and watch LEDs, servos, buzzers, and sensors react with physics and realistic wiring. Live delay() updates.
What is it?
I built this because I wanted an Arduino simulator that felt more alive than the tools I had tried before. The idea is simple: write real Arduino C++ in the browser, compile it on the backend, and then run the result inside a 3D simulation where LEDs, servos, buzzers, and other parts react in real time.
What made the project interesting is that it crosses a lot of layers. There is a compiler service, there is a browser-side execution environment, and there is a visual simulation layer that has to stay in sync with the running code.
The compiler service
The backend exposes a compile endpoint where I send the sketch code and the selected board. The server creates a temporary build directory, writes the Arduino sketch file, invokes `arduino-cli`, and returns the compiled `.hex` output.
I also wanted useful compile feedback without making the API response awkward, so I returned the binary separately and pushed the build log into a response header. That kept the main response clean while still giving the frontend enough information to show errors or warnings when compilation failed.
UUID isolation per job
One detail that mattered more than I expected was per-request isolation. If two compile jobs share the same build directory or sketch filename, they can overwrite each other and create nonsense outputs. That kind of bug is annoying because it only appears once concurrent traffic shows up.
So every compile request gets its own UUID-based temp directory. That means each build is isolated, cleanup is straightforward, and parallel compiles do not step on each other. It is a small design choice, but it turns a flaky compiler service into a stable one.
3D frontend: Three.js + WebAssembly
Once the sketch is compiled, I run the resulting program inside the browser through WebAssembly. The simulation layer reads state out of the running program frame by frame and maps it onto 3D components. If the sketch turns an LED on, the scene updates. If a servo angle changes, the model rotates.
The really nice part is that this lets me separate logic from rendering. The Arduino code behaves like firmware logic, while Three.js is just visualizing the current state. I also added a hook around `delay()` so timing can be adjusted interactively without recompiling every tiny experiment.
mBlock support
I also wanted the simulator to be usable by beginners, not just by people already comfortable with C++. That is where mBlock support came in. Users can build logic with drag-and-drop blocks, and the system still feeds into the same simulation path.
I like that because it makes the product a bridge instead of a dead end. Someone can start with blocks, understand the behavior visually, and later move into real Arduino code while staying inside the same environment.
Key takeaways
- arduino-cli programmatic usage: FQBN board identifiers, compile flags, build paths
- UUID-per-job directory isolation for parallel compile requests
- Build artifacts in HTTP headers: returning binary body + metadata without multipart
- Emscripten: compiling C++ to WebAssembly, timing shims for delay()
- Three.js emissive materials and per-frame state sync from WASM for hardware simulation