1. Quick start
- Open the Studio. It loads with a Blink circuit already wired and a starter sketch in the editor.
- Click Run. The sketch compiles on our server, downloads as Intel HEX, and runs in a real
ATmega328pemulator (avr8js). The on-board L13 LED starts blinking; pin13on the virtual Uno toggles every 500 ms. - Click Demos ▾ to load any of the bundled circuits + sketches. Each demo clears the canvas, places the components, wires them, and loads the matching sketch.
2. The Studio
The Studio is a single SVG canvas that holds three layers:
- Components — Arduino, breadboards, resistors, LEDs, sensors. Click a part in the palette and drop it on the canvas (or tap to place at canvas centre).
- Wires — click one pin then another to wire them. Pins glow when the mouse is within 14 px; the wire snaps to the nearest pin.
- Overlay — the selection ring, drag ghost, and pin highlights.
The Parts palette on the left has a search box (type "uno", "led", "resistor"…) and collapsible category headers. Categories include Boards · Prototyping · Inputs · Sensors · Outputs · Drivers · Passives · Active.
The Inspector on the right shows the selected component's props: resistor value, LED colour, pot wiper value, LDR brightness, capacitor capacitance, etc. Sliders update live while a sketch is running — twist a pot and the AVR sees the new analogRead() value on the next loop iteration.
Below the canvas: the code editor + serial monitor.
3. View modes
Three view buttons in the canvas toolbar:
- Breadboard — pseudo-physical view. Components look like real parts.
- Schematic — symbol view (resistor zigzag, transistor triangle, etc.). Wires unchanged.
- JSON — Wokwi-inspired editable diagram document. Edit it and click Apply to replace the live circuit. Useful for templating, sharing, or batch edits. Format spec below.
4. Sketch editor
Plain <textarea> with monospace font. Standard Arduino C++. Compiled by arduino-cli on our own Hetzner box using --fqbn arduino:avr:uno; resulting HEX cached by sketch hash so repeat runs are instant.
- Reset button restores the starter sketch.
- Copy button puts the current sketch on your clipboard.
- Cmd/Ctrl + Enter runs the sketch from inside the editor.
- Tab inserts two spaces.
- Autosaved to
localStorageon every change.
Standard libraries already installed on the build server: LiquidCrystal, Servo. Need another? Open an issue or use the contact link.
5. Serial monitor
USART output from the AVR streams in real time. Cmd/Ctrl+Enter from the editor runs the sketch and clears the monitor. Bytes are accumulated and decoded as UTF-8 on newline, so °, ≈, µ, em-dashes, and emoji all render correctly. Capped at 500 lines so a runaway Serial.println() loop can't grow the page.
Copy button on the monitor dumps the visible lines (with timestamps) to your clipboard.
6. Demos
Each demo loads a fully-wired circuit AND the matching sketch. Below: the topology behind the simplest one — Blink.
The complete demo list:
- Blink — D13 → 220Ω → LED → GND. The Hello World.
- PWM LED Fade —
analogWrite()on D9 makes the LED breathe. - RGB Color Mix — Common-cathode RGB on D9 / D10 / D11.
- Live Pot → analogRead — Twist the pot, watch Serial.
- Button → LED —
INPUT_PULLUPidiom. - Buzzer melody — C-major scale with
tone(). - LDR night-light — Photoresistor → PWM brightness.
- Knight Rider — Six LEDs on D2–D7 swept back and forth.
- NPN Low-Side Switch · PNP High-Side Switch — Discrete transistor demos.
- Capacitor blocks DC — Demonstrates the cap-as-open at DC steady-state.
- LCD 16×2 Hello — Scrolling text + live
millis()counter. - HC-SR04 Distance — Ultrasonic ping; slider sets simulated range.
- Pot → Servo —
analogReaddrivesServo.writein real time. - Relay click — D7 toggles a 5 V coil once per second.
- Thermistor → A0 — 10 kΩ NTC with Steinhart Beta-equation decoding.
- DC Motor + L293D — Direction + PWM speed via H-bridge.
7. Component reference
Every part exposes a type, size, pins, defaultProps, a render() and (sometimes) an onCircuitChange() callback. Pins always have an id; many have a dir (up / down / left / right) that drives the Manhattan wire router.
| Type | Pins | Notes |
|---|---|---|
uno | D0–D13 (top), VIN / 5V / 3V3 / GND / GND2 / RST / IOREF / A0–A5 (bottom) | The ATmega328p emulator; output pins drive 5V through a 25 Ω internal R. |
breadboard | tA-row-col / bA-row-col + power rails | Half-rows in standard 5-tie / 5-tie layout. |
led | A, K | Forward-biased only when A is higher than K. Brightness scales with current; burns out above 35 mA. |
rgb_led | R, G, B, C (common cathode) | Per-channel PWM mixing. |
resistor | 1, 2 | props.ohms sets the value. Used in the DC solver; values matter. |
cap_ceramic / cap_electrolytic | 1, 2 / +, - | props.value string ("100nF", "10uF", "470µF"). Integrated over time with backward-Euler. |
npn / pnp | C, B, E | Modelled as a switch with VCE,sat ≈ 0.2 V (2 Ω on-resistance) when conducting. |
button | 1, 2 | Clicked = pins shorted. Two-position. |
pot | 1, W, 3 | Inspector slider sets props.value (0–1023). Wiper feeds the connected analog pin. |
ldr | 1, 2 | Inspector "brightness" maps directly to ADC reading. |
thermistor | 1, 2 | 10 kΩ NTC; inspector temperature → ADC via piecewise NTC table. |
buzzer | +, - | Web Audio square-wave oscillator at the AVR's measured pin-toggle frequency. |
motor | M1, M2 | Spin direction + RPM derived from differential voltage / PWM. |
servo | VCC, SIG, GND | Pulse-width decoded to 0–180°; horn rotates. |
h_bridge | VCC, GND, ENA, IN1, IN2, OUT1, OUT2 | L293D-style. Forward / reverse / brake. ENA PWM passes through to outputs. |
relay | VCC, IN, GND, COM, NO, NC | IN HIGH → coil energised → status LED on, COM ↔ NO. |
hc_sr04 | VCC, TRIG, ECHO, GND | Inspector distance (cm). TRIG falling edge → ECHO pulse of distance × 58 µs scheduled cycle-accurately. |
lcd | VSS, VDD, RS, E, D4–D7 | HD44780 4-bit. Tracks E falling edge to latch nibbles. Standard LiquidCrystal sketches work. |
8. JSON diagram format
Switch to JSON view to see (and edit) the circuit as a Wokwi-inspired document:
{
"version": 1,
"parts": [
{ "id": "c0", "type": "uno", "x": 40, "y": 280, "rot": 0, "attrs": {} },
{ "id": "c1", "type": "resistor", "x": 440, "y": 100, "rot": 0, "attrs": { "ohms": 220 } },
{ "id": "c2", "type": "led", "x": 620, "y": 180, "rot": 0, "attrs": { "color": "red" } }
],
"connections": [
["c0:D13", "c1:1", "auto"],
["c1:2", "c2:A", "auto"],
["c2:K", "c0:GND", "auto"]
]
}
Fields:
version— always1.parts[].id— unique string. The serializer uses"c<n>"but anything works.parts[].type— one of the catalog names from the component table (uno,led,lcd, …). Wokwi'swokwi-arduino-unoprefix is not mapped — use our names directly.parts[].x/y— canvas-space pixels.parts[].rot— 0 / 90 / 180 / 270 degrees.parts[].attrs— the same object aspropson the live component (resistorohms, LEDcolor, capvalue, etc.).connections[]—["partId:pinId", "partId:pinId", color]. The color string is informational;"auto"lets the studio pick from its palette.
Hit Apply to swap the canvas for the parsed JSON. Validation runs first: unknown types, duplicate IDs, malformed endpoints all surface as errors before anything changes.
Wokwi's wire mini-language ("v10", "h-5", "*") is parsed but ignored — the studio's Manhattan auto-router places every wire.
9. Circuit solver — what's actually simulated
Resistor values, LED forward voltages, transistor saturation, and capacitor charge are all modelled by a Modified Nodal Analysis solver that runs on every pin event plus a 33 Hz tick when capacitors are present. Lives in assets/js/circuit-solver.js.
Specifically:
- Resistors use their real
ohms. Swap 220 Ω for 47 Ω and the LED gets visibly brighter; swap for 10 kΩ and it dims. - LEDs are piecewise-linear diodes with per-colour Vf (red 2.0 V, green 2.2 V, blue 3.1 V) and 10 Ω on-resistance. Current ≥ 35 mA flags the LED
friedand the body re-renders burnt. - Voltage sources — Uno's 5V / 3V3 / VIN rails are ideal sources. Output pins are 5 V (HIGH) / 0 V (LOW) through a 25 Ω internal R that matches the AVR's output stage.
- Transistors — NPN / PNP modelled as a 2 Ω switch when conducting (VCE,sat ≈ 0.2 V at typical Ic). Base decision still binary.
- Capacitors — backward-Euler transient integration.
props._capVpersists across solves. RC time constants are real (try a 470 µF cap with a 1 kΩ resistor and watch the LED fade).
Deliberately not modelled: inductors, AC analysis, op-amps, Shockley diode equation, h-parameter transistors. Open the About page or the roadmap for current scope.
10. Keyboard shortcuts
| Key | Action |
|---|---|
| Cmd / Ctrl + Z | Undo |
| Cmd / Ctrl + Shift + Z · Cmd+Y | Redo |
| Cmd / Ctrl + Enter | Run sketch (from inside the editor) |
| R | Rotate selected component 90° |
| D | Duplicate selected component |
| Del / Backspace | Delete selected component or wire |
| Esc | Cancel in-progress wire / deselect |
| Cmd / Ctrl + scroll | Zoom canvas |
| Pinch (touch) | Zoom canvas; two-finger drag pans |
11. Known limits
- Voltage dividers with resistor-as-conductor demos still work because the solver sees real resistors — but the live cell at
analogReadgoes through the studio's pinSources path, which doesn't run through the solver. Wire a pot to a real voltage divider and twist it: the AVR reads the slider directly, not the divided voltage. - Mobile drag-drop works but the canvas is busy on a small screen. Pinch-zoom helps. The action toolbar gets a phone-specific layout at ≤ 1080 px.
- Compile errors come from
avr-gccviaarduino-clion our server. If our endpoint is down, the runner falls back to hexi.wokwi.com. - The HEX cache is keyed by sketch text. Adding a comment changes the digest. Identical sketches return instantly on second run.
- tone() pitch depends on the AVR actually toggling the pin at audio rate.
noTone()stops it. Multiple buzzers play independently.