Barrel Synth - A Feedback-Based Browser Instrument

Josh Mitchell 2022

This "browser instrument" takes inspiration from a couple of places. First, the synthesis method being used is heavily related to Tom Mudd's Gutter Synthesis algorithm. One Gutter Synthesis voice is made of a chaotic duffing oscillator with some feedback through a modal resonator, which loosely constrains the oscillator to a more discernible pitch, while still allowing for some of the more interesting aspects of the duffing oscillator to shine through. I've used a different chaotic oscillator and a comb filter instead of a modal resonator for a slightly different flavor of frequency-constraining. Lastly, the individual voices in this instrument interact with each other through a slightly different method than in the Gutter Synthesis algorithm.

This type of interaction and the interface for controlling it are more inspired by my experience with no-input mixing boards and matrix mixing. Both of these approaches to performance, though especially the former, present the user with a feedback-focused interface with a limited set of highly-related controls. In the case of a stand-alone matrix mixer, the only controls available are for routing external inputs to other places, which led me to the idea to create an instrument where the only controls available were for feedback paths between a group of uncontrollable sound-generators. Can this still be an expressive instrument? I think so!

The interface below is set up as a grid of sliders, with rows corresponding to outputs and columns corresponding to inputs, and in the last column, the final mix out to your ears. The sliders determine how much of any output is sent to a particular input, and are initialized with each output being fed back maximally to its own input, and nowhere else. Hope you enjoy it!

A to A A to B A to C A to D A to E A to Output

B to A B to B B to C B to D B to E B to Output

C to A C to B C to C C to D C to E C to Output

D to A D to B D to C D to D D to E D to Output

E to A E to B E to C E to D E to E E to Output

live code
// Parameters controlled by the sliders 0 => global float vola; 0 => global float volb; 0 => global float volc; 0 => global float vold; 0 => global float vole; 100 => global float aa; 0 => global float ab; 0 => global float ac; 0 => global float ad; 0 => global float ae; 0 => global float ba; 100 => global float bb; 0 => global float bc; 0 => global float bd; 0 => global float be; 0 => global float ca; 0 => global float cb; 100 => global float cc; 0 => global float cd; 0 => global float ce; 0 => global float da; 0 => global float db; 0 => global float dc; 100 => global float dd; 0 => global float de; 0 => global float ea; 0 => global float eb; 0 => global float ec; 0 => global float ed; 100 => global float ee; // Variables used to make audio outs for each channel 0.5 => float xa; 0.5 => float xb; 0.5 => float xc; 0.5 => float xd; 0.5 => float xe; // Audio paths for each channel Gain mvol => JCRev rev => dac; rev.mix(0.0); // Reverb can be fun! Step ina => Gain hacka; hacka => DelayL dela => blackhole; OnePole polea => Gain gaina => mvol; Step inb => Gain hackb; hackb => DelayL delb => blackhole; OnePole poleb => Gain gainb => mvol; Step inc => Gain hackc; hackc => DelayL delc => blackhole; OnePole polec => Gain gainc => mvol; Step ind => Gain hackd; hackd => DelayL deld => blackhole; OnePole poled => Gain gaind => mvol; Step ine => Gain hacke; hacke => DelayL dele => blackhole; OnePole polee => Gain gaine => mvol; // Main and channel volume controls mvol.gain(1); hacka.gain(0); vola/100 => gaina.gain; hackb.gain(0); volb/100 => gainb.gain; hackc.gain(0); volc/100 => gainc.gain; hackd.gain(0); vold/100 => gaind.gain; hacke.gain(0); vole/100 => gaine.gain; // Resonators for each channel Gain fba; fba => dela => polea => fba; polea.gain(0.15); 15::ms => dela.delay; Gain fbb; fbb => delb => poleb => fbb; poleb.gain(0.15); 15.3::ms => delb.delay; Gain fbc; fbc => delc => polec => fbc; polec.gain(0.15); 10::ms => delc.delay; Gain fbd; fbd => deld => poled => fbd; poled.gain(0.15); 0.75::ms => deld.delay; Gain fbe; fbe => dele => polee => fbe; polee.gain(0.15); 80::ms => dele.delay; // Update the chaotic maps every 10 samples while (1) { // For each channel: vola/100 => gaina.gain; // Read volume slider xa*(1-(dela.last()*aa/135))*(1-(delb.last()*ba/135)) => xa; // Read feedback xa*(1-(delc.last()*ca/135))*(1-(deld.last()*da/135)) => xa; // Read feedback xa*(1-(dele.last()*ea/135)) => xa; // Read feedback xa*3.8*(1-xa) => xa; // The logistic map xa => hacka.gain; // Semi-audio-rate output with limiting // And repeat for the other channels: volb/100 => gainb.gain; xb*(1-(dela.last()*ab/135))*(1-(delb.last()*bb/135)) => xb; xb*(1-(delc.last()*cb/135))*(1-(deld.last()*db/135)) => xb; xb*(1-(dele.last()*eb/135)) => xb; xb*3.85*(1-xb) => xb; xb => hackb.gain; volc/100 => gainc.gain; xc*(1-(dela.last()*ac/135))*(1-(delb.last()*bc/135)) => xc; xc*(1-(delc.last()*cc/135))*(1-(deld.last()*dc/135)) => xc; xc*(1-(dele.last()*ec/135)) => xc; xc*3.9*(1-xc) => xc; xc => hackc.gain; vold/100 => gaind.gain; xd*(1-(dela.last()*ad/135))*(1-(delb.last()*bd/135)) => xd; xd*(1-(delc.last()*cd/135))*(1-(deld.last()*dd/135)) => xd; xd*(1-(dele.last()*ed/135)) => xd; xd*3.95*(1-xd) => xd; xd => hackd.gain; vole/100 => gaine.gain; xe*(1-(dela.last()*ae/135))*(1-(delb.last()*be/135)) => xe; xe*(1-(delc.last()*ce/135))*(1-(deld.last()*de/135)) => xe; xe*(1-(dele.last()*ee/135)) => xe; xe*3.8*(1-xe) => xe; xe => hacke.gain; 10::samp => now; }