A monolithic audio engine for live coding ported from C to Rust and then extended by BuboBubo. The initial project is called Dough and was designed by Felix Roos and al. Doux can run in a web browser via WebAssembly, or natively using an an OSC server and/or a REPL. Doux is an opinionated, fixed path, semi-modular synth that is remote-controlled via messages. It has been designed initially to be used with Strudel. This fork is a bit special: it adapts and specialize the engine for integration with Sova and Cagire, two live coding environments built with Rust.

This project is AGPL 3.0 licensed. We encourage you to support the development of the original version through the TidalCycles Open Collective. Consult the support page for more information.

License & Support

Getting Started

Click anywhere on the page to start the audio context. Then click inside a code block and press Ctrl+Enter to run it, or Escape to stop. You can also press the play button if you don't want to edit and just want to preview. The easiest way to start making sound with Doux is just to specify a sound to use:

You can set the pitch with the /note parameter (MIDI note numbers):

Or use frequency directly with /freq:

Omitting parameters

It is possible to omit pretty much all parameters or to under-specify the parameters a synthesis voice you wish to play. Doux has preconfigured defaults for most of the core parameters.

If no sound source is specified, the voice defaults to tri with sensible envelope defaults. If a sound name is not recognized, the voice is silently skipped.

Sound Sources

There are multiple sound sources you can use, detailed in the reference. You can also import your own audio samples or use a live input as a source.

Turn on your microphone (and beware of feedback!):

Envelopes

The amplitude envelope controls how the sound fades in and out. It uses a DAHDSR model: delay, attack, hold, decay, sustain, release.

/attack is the time (in seconds) to reach full volume.
/decay is the time to fall to the sustain level.
/sustain is the level held while the note is on (0-1).
/release is the time to fade to silence after note off.

Try changing each parameter to hear how it affects the sound.

Filters

Most of the default sources are producing rich timbres full of harmonics. Use filters to remove some components of the spectrum. Doux has all the basic filters you need:

All filters come with a control over resonance /..q:

Effects

Doux includes several effects. Here is a sound with reverb:

And another sound with delay:

If you stack up effects, it can become quite crazy:

Note that the order in which the effects are applied is fixed by default! You cannot re-order the synthesis chain.

Inline Modulation

Many parameters support inline modulation: instead of a static value, you write an expression with an operator that describes the motion between two values. In the reference, parameters marked with ~ support this syntax.

Oscillate ~

Cycle between two values forever. Syntax: min~max:period[shape]

  • 200~4000:2 β€” sine (default)
  • 200~4000:2t β€” triangle
  • 200~4000:2w β€” saw (ramp up, snap down)
  • 200~4000:2q β€” square (alternate min/max)

A lowpass filter sweeping from 200Hz to 4000Hz over 2 seconds:

Pan bouncing left to right with a triangle wave:

Transition >

Go from A to B over a duration, then hold at B. Syntax: start>target:duration[curve]

  • 200>4000:2 β€” linear (default)
  • 200>4000:2e β€” exponential
  • 200>4000:2s β€” smooth (ease in-out)
  • 200>4000:2i β€” swell (slow start, fast finish)
  • 200>4000:2o β€” pluck (fast start, slow settle)
  • 200>4000:2p β€” stair (8 discrete steps)

Append ~ to loop the transition:

  • 200>4000:2~ β€” looping ramp

FM index ramping up exponentially over 3 seconds:

Slew > (no start)

Transition from the current parameter value to a target. Same as transition but without specifying the start β€” the voice reads its own value. Ideal for persistent voices (voice/0 + gate/0). Syntax: >target:duration[curve]

  • >4000:0.2 β€” slew to 4000 over 0.2s (linear)
  • >800:0.5e β€” slew to 800 exponentially
  • >1200:0.1s β€” slew to 1200 with S-curve

Start a persistent voice, then smoothly update its filter cutoff:

Envelope ^

A gate-aware DAHDSR envelope between two values. Starts on note trigger, sustains until gate-off, then releases. Syntax: min^max:attack[:decay[:sustain[:release]]]

  • 200^8000:0.01:0.1:0.5:0.3 β€” full ADSR
  • 200^8000:0.01 β€” attack only (sustain at max)
  • 0^5:0.01:0.1:0.3:0.5 β€” FM index envelope

Filter cutoff with envelope modulation:

FM index with percussive envelope:

Random ?

Wander stochastically within a range. Syntax: min?max:period[shape]

  • 200?4000:0.5 β€” sample-and-hold (default)
  • 200?4000:0.5s β€” smooth (cosine-interpolated)
  • 200?4000:0.1d β€” drunk walk (brownian)

Random pan jumps: