A small language for building and simulating logic circuits, made for people learning how computers work.

source
// half_adder.circ
import xor "<builtin>/xor.circ"
input a, b
xor s(a=a, b=b)
and c(a=a, b=b)
output sum(in=s.out)
output carry(in=c.out)
circ-compile --preview
╭───╮     ╭───╮         ╭───────╮
│ a ├○─●─▶┤   │ ╭──────▶┤ carry │
╰───╯  │  │AND├○╯       ╰───────╯
      ╭┼─▶┤   │
      ││  ╰───╯
      ││
╭───╮ ││  ╭───────╮     ╭─────╮
│ b ├○●╰─▶┤       │ ╭──▶┤ sum │
╰───╯ │   │[xor:s]├○╯   ╰─────╯
      ╰──▶┤       │
          ╰───────╯

A half-adder. Two inputs, an XOR macro for the sum, an AND for the carry.

live simulation click input pins to toggle
↓ Download circ-compile for Linux, macOS, or Windows

What it is

circ is a declarative language. Programs are flat lists of declarations: name an input pin, instantiate a gate, wire its ports to signals from other components. The primitives are and, not, led, and wire; macros for or, nand, nor, xor, and xnor expand to those primitives at compile time. Larger circuits live in their own .circ file and get pulled in with import.

What it isn't

circ is a v0. There are no multi-bit buses, no clocked registers, no analog signals, no tri-state lines. A wire is a single-bit pass-through, not a let-binding for a vector. The language deliberately stops at the same boundary as the early Nand2Tetris hardware chapters.

Where it runs

circ-compile produces a self-contained WebAssembly module: drive its input pins from JavaScript, read its outputs back, run it from Node or a browser. There's also a --preview flag that prints an ASCII schematic to your terminal — handy for sanity-checking the wiring before you simulate.

If you've enjoyed Nand2Tetris or building NANDs from scratch in Petzold's Code, this is a language for doing more of that.