Published
Edited
Nov 3, 2021
Insert cell
# 13 Effects demo
## Frontend masters [Web-Audio by Matt DesLauriers](https://github.com/mattdesl/workshop-web-audio) (tone.js)

[Video Replay only available as editing still happening](https://frontendmasters.com/workshops/web-audio/).


Insert cell
volume = -15
Insert cell
## docs of interest:

https://tonejs.github.io/docs/r13/Synth
https://tonejs.github.io/docs/r13/Oscillator [Types of Oscillator.](https://tonejs.github.io/docs/r13/Oscillator#:~:text=%3C/%3E-,.type,-%E2%86%9D%20String%20%23)

[Reverb](https://tonejs.github.io/docs/r13/Reverb): [WET](https://tonejs.github.io/docs/r13/Reverb#wet)
.[generate](https://tonejs.github.io/docs/r13/Reverb#generate)() RETURNS a promise which returns *this* object.

Generate the Impulse Response. Returns a promise while the Impulse Response is being generated.

.[Filter](https://tonejs.github.io/docs/r13/Filter)()

https://tonejs.github.io/docs/r13/FatOscillator

https://tonejs.github.io/docs/r13/FatOscillator#connect

still looking for more docs on the oscillator prefix 'fat' that spreads the oscillator over multiple frequencies.

https://tonejs.github.io/docs/r13/Oscillator#connect
Insert cell
p5(sketch => {
// The setup() function is async
let ready = false;

// Can be 'sine', 'sawtooth', 'triangle', 'square' Can also add suffixes like sine8, square4
const type = "square";

let filter, effect; // The filter and effect nodes which we will modulate

// Min and max frequency (Hz) cutoff range for the filter
const filterMin = 100;
const filterMax = 5000;

// 0..1 values for our FX
let fxU = 0.5;
let fxV = 0.5;

const notes = ["C5", "A3", "D4", "G4", "A4", "F4"]; // The notes we will use


let synth // The synth that plays notes

let PI = sketch.PI

const dim = Math.min(width, height)

//----------------------------------------------------------------------
// Create a new canvas to the browser size
sketch.setup = async function() {
// async function setup() {
sketch.createCanvas(width, height);

sketch.background("#001b42");

// Make the volume quieter
Tone.Master.volume.value = volume;

// Setup a reverb with ToneJS
const reverb = new Tone.Reverb({
decay: 5,
wet: 0.5,
preDelay: 0.2,
});

// Load the reverb
await reverb.generate();


effect = new Tone.FeedbackDelay(0.4, 0.85); // Create an effect node that creates a feedback delay

// Create a new filter for the X slider
filter = new Tone.Filter();
filter.type = "lowpass";

// Setup a synth with ToneJS
synth = new Tone.Synth({
oscillator: { // We prefix 'fat' so we can spread the oscillator over multiple frequencies
type: `fat${type}`,
count: 3,
spread: 30,
},
envelope: {
attack: 0.001,
decay: 0.1,
sustain: 0.5,
release: 0.1,
attackCurve: 'exponential' ,
// decayCurve: 'exponential' ,
// releaseCurve: 'exponential',
},
});

// Now lets wire up our stack like so: synth->effect->reverb->filter->master
synth.connect(effect);
effect.connect(reverb);
reverb.connect(filter);
filter.connect(Tone.Master);

// Now we're ready for drawing!
ready = true;
}

//---------------------------------------------------------------------
// Render loop that draws shapes with p5
sketch.draw = function() {

// Make sure async setup() is done before we draw
if (!ready) return;

filter.frequency.value = sketch.lerp(filterMin, filterMax, fxU);
effect.wet.value = fxV;

// Black background
// sketch.background(0, 0, 0, 20);
sketch.background("#001b4214");

// draw the two FX knobs
if (sketch.mouseIsPressed) {
sketch.noFill();
sketch.strokeWeight(dim * 0.0175);
sketch.stroke(255);
drawEffectKnob(dim * 0.4, fxU);
drawEffectKnob(dim * 0.6, fxV);
}

// Draw a 'play' button
sketch.noStroke();
sketch.fill(255);
polygon(width / 2, height / 2, dim * 0.1, 3);
}

//------------------------------------------------------------------------------------------
// Draws an arc with the given amount of 'strength'
function drawEffectKnob(radius, t) {
if (t <= 0) return;
sketch.arc(width / 2, height / 2, radius, radius, 0, PI * 2 * t);
}

// Update FX values based on mouse position
function updateEffects() {
fxU = sketch.max(0, sketch.min(1, sketch.mouseX / width));
fxV = sketch.max(0, sketch.min(1, sketch.mouseY / height));
}

// ----------------------------------------------------------
// Update the FX values
sketch.mouseDragged = function () {
updateEffects();
}

// Trigger synth OFF
sketch.mouseReleased = function () {
if (synth) synth.triggerRelease();
}

// Update the FX and trigger synth ON
sketch.mousePressed = function () {
updateEffects();
if (synth) synth.triggerAttack(sketch.random(notes));
}

// -----------------------------------------------------------------------------------
// Draw a basic polygon, handles triangles, squares, pentagons, etc
function polygon(x, y, radius, sides = 3, angle = 0) {
sketch.beginShape();
for (let i = 0; i < sides; i++) {
const a = angle + sketch.TWO_PI * (i / sides);
let sx = x + Math.cos(a) * radius;
let sy = y + Math.sin(a) * radius;
sketch.vertex(sx, sy);
}
sketch.endShape(sketch.CLOSE);
}

})


Insert cell
height = width/3
Insert cell
risoColors = FileAttachment("riso-colors.json").json()
Insert cell
Tone = require('tone')
Insert cell
import {p5} from "@tmcw/p5"
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more