Dancing Cairo circles (with music!)
Made for a TilingTuesday post on Mastodon.
Click somewhere on the page to start the music.
let synth
function setup() {
createCanvas(400, 400);
synth = new p5.PolySynth()
synth.setADSR(0.05, 0.05, 0.5, 0.2)
windowResized()
describe('Against a gray background, white circles positioned on the vertices of a snub square tiling take turns randomly swapping with one of their 5 neighboring circles, with one circle going in front and the other one passing behind it. On occasion, some circles are left without a "partner" and stand still for a turn. Every swap, a note plays in sequence.')
}
function windowResized() {
// resizeCanvas(windowWidth, windowHeight)
}
function mousePressed() {
userStartAudio();
}
let l = 0.8
let t = l
let c = 0
let swaps = {}
let fronts = {}
let notes = ['A4', 'D4', 'E4', 'F#4', 'C#4', 'G4']
let notes2 = ['D3', 'F#3', 'C#3', 'A3', 'G3', 'E3']
function draw() {
background(100);
t += deltaTime / 1000 // 1 / 30
if (t > l) {
c ++
t %= l
// ABACABA sequence
let note = notes.find((_, i) => c & (1 << i)) || notes[notes.length - 1]
synth.play(note, 0.5, l - t + l / 2, 0.3)
if ((c - 1) & 16) {
let note2 = notes2.find((_, i) => (c) & (1 << i)) || notes[notes.length - 1]
synth.play(note2, 0.5, l - t, 0.3)
}
swaps = {}
}
let f = 0.8
let b = (1 - cos(t / l * PI)) / 2
let br = sqrt(b * (1 - b))
let r = 35
let d = 100
let d1 = 18
let d2 = 35
function node(x, y, front) {
// magic starts here
let cx = floor(x/d) * d + d/2
let cy = floor(y/d) * d + d/2
let k1 = (cx - x) == d1 ? 1 : (cx - x) == -d1 ? -1 : 0
let k2 = (cy - y) == -d1 ? 1 : (cy - y) == d1 ? -1 : 0
let neighbors = [
[cx + (cy - y), cy - (cx - x)],
[cx - (cy - y), cy + (cx - x)],
[cx - k1 * d + (cy - y), cy + k2 * d - (cx - x)],
[cx - k2 * d - (cy - y), cy - k1 * d + (cx - x)],
[cx - k2 * d + (cx - x), cy - k1 * d + (cy - y)]
]
// magic ends here
if (!swaps[[x, y]]) {
let filtered = neighbors.filter(x => !swaps[x])
if (filtered.length) {
// pick a neighbor to swap with
// swaps[[x, y]] = filtered.find((_, i) => c & (1 << i)) || filtered[filtered.length - 1]
swaps[[x, y]] = random(filtered)
// swaps[[x, y]] = filtered[c % filtered.length]
swaps[swaps[[x, y]]] = [x, y]
fronts[[x, y]] = !fronts[[x, y]]
fronts[swaps[[x, y]]] = !fronts[[x, y]]
} else {
swaps[[x, y]] = [x, y]
fronts[[x, y]] = random([true, false])
}
}
// render animation
if (fronts[[x, y]] == front) {
let sx = lerp(x, swaps[[x, y]][0], b)
let sy = lerp(y, swaps[[x, y]][1], b)
let sr = r * pow(f, fronts[[x, y]] ? br : -br)
strokeWeight(1)
stroke(100)
fill(lerp(245, 255, 0.5 - (fronts[[x, y]] ? br : -br)))
circle(sx, sy + 5 * br * (front ? -1 : 1), sr)
}
}
// let seed = random(1, 1000000)
// let seed2 = random(1, 1000000)
for (let front of [true, false]) {
for (let x = -d; x < width + d; x += d) {
for (let y = -d; y < height + d; y += d) {
// randomSeed(x + y % 2 ? seed : seed2)
node(x + d/2 - d1, y + d/2 - d2, front)
node(x + d/2 + d1, y + d/2 + d2, front)
node(x + d/2 + d2, y + d/2 - d1, front)
node(x + d/2 - d2, y + d/2 + d1, front)
}
}
}
}(Originally seen at https://editor.p5js.org/bojidar-bg/sketches/tMCeEmqO2)
Browse more articles?
← Catenary chain Experiments tagged p5 (61/85) Noise visualizer →
← MIDI Visualizer Experiments tagged music (2/2) →|
← Spinny hexagon/hexagon-star tiling Experiments tagged tiling (6/6) →|
← Catenary chain Experiments on this site (61/85) Noise visualizer →