Simten

A Synthesiser Made of Logic Gates

A 16-bit counter, a table of numbers and one multiplier. No oscillator object, no filter node — every sample you hear is computed by a circuit you can open up, and the same netlist runs on an FPGA.

Interactive tutorial/~8 min read/Built with Simten

Watch it make the sound

One simulation drives everything below. The trace is the circuit’s audio output, sample by sample; the speakers are fed from the same net. Not a visualisation of the sound — the sound.

Both controls are input ports on the circuit, not settings in the page. Changing one sets a signal on the next clock tick — nothing recompiles, nothing reloads. The same two ports exist when this is exported to Verilog, so the knobs survive onto an FPGA.

audio — 0 ticks elapsed
wave11high address bits into the ROM

All four waveforms live in one ROM, laid end to end, and wave supplies the two high address bits — so picking a timbre is choosing which bank the phase accumulator reads from. Each table is band-limited: only the harmonics that fit under the Nyquist frequency are summed. Generate a sawtooth the naive way at 22 kHz and its upper harmonics fold back down as inharmonic clangour, which is a property of the table rather than of the circuit.


What you just heard

Sixteen nodes. A register and an adder make the phase accumulator: every tick it adds inc to itself and wraps at 216, so inc is the pitch. The top twelve bits index the wavetable, and the four discarded bits are the fractional part — a zero-order hold, exactly the shortcut the era took.

The table holds 0–255, so a subtractor recentres it to two’s complement before the envelope touches it. Skip that and the envelope would be scaling a DC offset rather than a waveform, and every note would click.

The envelope is the cluster on the right: subtract a step each tick, a comparator and mux clamp it at zero rather than letting it wrap, and a second mux reloads it to full when trig goes high for one tick. Then one signed multiply scales the sample. Everything between the two registers is combinational, which is why one clock tick produces exactly one audio sample.

Nothing above is a setting in the page. wave and decay are input ports, so changing one drives a signal into the circuit on the next tick — the same way a knob on a real synth does. Had they been Constant nodes they would have been hardwired the moment this was synthesised, and the knobs would exist only in the browser.