const color0 = [11, 21, 20];
const color1 = [44, 5, 91];
const color2 = [18, 80, 91];
function silence(t, period) {
'C4': (t, period) => note(261.63, t, period),
'C#4': (t, period) => note(277.18, t, period),
'Eb4': (t, period) => note(311.13, t, period),
'E4': (t, period) => note(329.63, t, period),
'G4': (t, period) => note(392.00, t, period),
'G#4': (t, period) => note(415.30, t, period),
'B4': (t, period) => note(493.88, t, period),
function note(freq, t, period) {
return sin((freq * PI * t) / period);
function cMinor(t, period) {
return notes["C4"](t, period) +
notes["Eb4"](t, period) +
function cSharpMinor(t, period) {
return notes["C#4"](t, period) +
function cSharpMinorSeventh(t, period) {
return cSharpMinor(t, period) +
return notes["C4"](t, period);
function eb4(t, period) {
return notes["Eb4"](t, period);
return notes["G4"](t, period);
function zeroToOneWave(x) {
return 0.5 * (sin(TAU * x - PI / 2) + 1);
const waves = [silence, c4, cSharpMinorSeventh, eb4, cMinor, g4, cSharpMinor, silence];
function drawLevels(r, roff, theta, sw, sPortion, c) {
strokeWeight(sw / sPortion);
let halfAngle = ((sw / 6) / (r - sw / 2)) / 2;
arc(0, 0, 2 * roff, 2 * roff, theta - halfAngle, theta + halfAngle);
arc(0, 0, 2 * roff, 2 * roff, theta + PI - halfAngle, theta + PI + halfAngle);
function drawRing(baseSize, r, rGroove, groovePortion, period, amp, elapsed) {
function drawWave(c, sw) {
for (let x = 0; x < 2 * r; x++) {
const y = w(x + elapsed, period) * amp;
const outSineWaveWeight = baseSize / 3;
const innerSineWaveWeight = baseSize / 6;
drawWave(color2, outSineWaveWeight);
drawWave(color0, innerSineWaveWeight);
strokeWeight(baseSize * 2);
circle(0, 0, (2 * (r + baseSize)));
strokeWeight(baseSize + 2);
let halfAngle = (outSineWaveWeight / (r - baseSize / 2)) / 2;
arc(0, 0, 2 * r, 2 * r, PI - halfAngle, PI + halfAngle);
arc(0, 0, 2 * r, 2 * r, -halfAngle, halfAngle);
halfAngle = (innerSineWaveWeight / (r - baseSize / 2)) / 2;
arc(0, 0, 2 * r, 2 * r, PI - halfAngle, PI + halfAngle);
arc(0, 0, 2 * r, 2 * r, -halfAngle, halfAngle);
strokeWeight(baseSize / groovePortion);
circle(0, 0, 2 * rGroove);
translate(width / 2, height / 2);
if (startMillis == null) {
const elapsed = millis() - startMillis;
const waveSwitchTime = 1500;
const duration = waves.length * waveSwitchTime;
const baseSize = width * 0.16;
const period = 261.63 * r;
const ampFactor = zeroToOneWave(elapsed / waveSwitchTime);
const amp = ampFactor * maxAmp;
if (ampFactor < epsilon && canSwitch) {
idx = (idx + 1) % waves.length;
} else if (ampFactor > epsilon) {
const rGroove = (r + (baseSize / 16));
drawRing(baseSize, r, rGroove, groovePortion, period, amp, elapsed);
const mPortion = groovePortion / 0.5;
const y = (wave()(elapsed, period) * amp);
drawLevels(r, mRadius, y / friction, mSize, mPortion, mColor);
drawLevels(r, mRadius, y * 2 / friction, mSize, mPortion, mColor);
drawLevels(r, mRadius, y * 4 / friction, mSize, mPortion, mColor);
drawLevels(r, mRadius, y * 8 / friction, mSize, mPortion, mColor);