A funny synthesizer using a simulation of the Bunimovich Stadium, a famous chaotic dynamical system. The slider controls the "amount of chaos" in the system.
Log in to post a comment.
// === BUNIMOVICH STADIUM === // by Alexis THIBAULT // The Bunimovich Stadium is a famous example of Dynamical Billiards, a mathematical problem where balls // bounce indefinitely against the walls of a given shape. // Here, the shape in question is a rectangle capped by two semicircles: it is one of the earliest examples // of a convex shape that exhibits chaotic behavior. // // A visualization of the bunimovich Stadium can be found here: // https://cscheid.net/projects/bunimovich_stadium/ // // In this ditty, we listen to the trajectory of a few balls as they bounce around. // The speed of each ball is such that it usually bounces at a rate corresponding to its frequency. // The "chaos" slider actually controls the width of the rectangle. input.chaos = 0.3; function sqNorm(x,y) { return x*x + y*y; } function reflect(vx, vy, nx, ny) { const dot = vx*nx + vy*ny; return [vx - 2*dot*nx, vy - 2*dot*ny]; } const bunimovichStadium = synth.def(class { constructor(options) { this.x = 0; this.y = 0; let theta = 2 * Math.PI * Math.random(); const freq = midi_to_hz(options.note); const spd = 4 * ditty.dt * freq; this.dx = spd * Math.cos(theta); this.dy = spd * Math.sin(theta); } process(note, tick, env, options) { const w = options.chaosAmount; this.x += this.dx; this.y += this.dy; if(this.x > w) { // Test whether we're in the circle of center (w,0), reflect if necessary let r2 = sqNorm(this.x-w, this.y); if(r2 > 1) { let r = Math.sqrt(r2); let xp = w + (this.x - w) / r; let yp = this.y / r; this.x = 2*xp - this.x; this.y = 2*yp - this.y; [this.dx, this.dy] = reflect(this.dx, this.dy, xp-w, yp); } } else if(this.x < -w) { // Test whether we're in the circle of center (-w,0), reflect if necessary let r2 = sqNorm(this.x+w, this.y); if(r2 > 1) { let r = Math.sqrt(r2); let xp = -w + (this.x + w) / r; let yp = this.y / r; this.x = 2*xp - this.x; this.y = 2*yp - this.y; [this.dx, this.dy] = reflect(this.dx, this.dy, xp+w, yp); } } else if (this.y > 1) { this.y = 1; this.dy *= -1; } else if (this.y < -1) { this.y = -1; this.dy *= -1; } const side = 0.2 * this.x / (1 + w); return [this.y + side, this.y - side]; } }, {env:one, amp:0.2, chaosAmount: () => 0.001 * 10000**input.chaos}); bunimovichStadium.play(c2, {pan:0}); bunimovichStadium.play(c3, {pan:-0.2}); bunimovichStadium.play(e4, {pan:0.2}); bunimovichStadium.play(g3, {pan:0.4}); bunimovichStadium.play(c4, {pan:-0.4});