#12

Interactive 3D Arduino Simulator

February 3, 2026

Three.jsC++WebAssemblyReactPhysics Engine

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?

A browser-based Arduino simulator. Users write real Arduino C++ code and watch LEDs blink, servos sweep, buzzers sound, and sensors trigger in a 3D environment — no hardware, no account. Built because Tinkercad's constraints were frustrating. The backend compiles Arduino sketches server-side via a Node.js Express API that shells out to arduino-cli.

The compiler service

POST /compile with { code, board } JSON body. The server: 1. Creates a UUID-namespaced temp directory under /tmp/arduino-builds/<uuid>/sketch/ 2. Writes the sketch to sketch.ino 3. Shells out: arduino-cli compile --fqbn <fqbn> --build-path <buildDir> <sketchDir> via execSync with a 60-second timeout 4. Reads the resulting .hex file 5. Returns the .hex as binary (octet-stream) 6. Returns the build log in the X-Build-Log response header (keeps the binary body clean) 7. Cleans up the temp directory

Supported boards: Uno, Nano, Mega, Micro, Nano (old bootloader).

UUID isolation per job

Every compile request gets its own directory under /tmp/arduino-builds/. Without this, two simultaneous compiles would write sketch.ino to the same path, corrupt each other's build, and produce garbage .hex files. UUID isolation means parallel compiles are completely independent. The temp dir is always deleted after the request — no disk accumulation.

3D frontend: Three.js + WebAssembly

The frontend runs the compiled Arduino code via Emscripten-compiled WebAssembly in the browser. The Arduino runtime (setup() + loop()) executes in WASM at 60fps. Component state (LED on/off, servo angle, sensor readings) is read from WASM memory each frame.

Three.js renders each component as a 3D mesh — LEDs are spheres with emissive material driven by WASM output, servos are boxes with rotation.z updated each frame, buzzers use the Web Audio API OscillatorNode. The live delay() tweak works because the WASM shim reads the current delay value from a JS-controlled variable on each loop iteration.

mBlock support

mBlock is Scratch-like block coding. Adding mBlock support means non-coders can drag-and-drop logic blocks and test them in the same 3D simulator. Internally, mBlock generates Arduino C++ from the block graph — the same Emscripten + Three.js pipeline runs unchanged. Students can start with blocks and transition to real C++ when ready.

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
Watch on YouTube →← all projects