Dyads

Creating music from two notes at a time when deploying websites.

Creating music from two notes at a time when deploying websites.
Composing with dyads (two notes) is a way to explore leading melodies and counter melodies in the simplest form. And, even in the simplest form, the results can be quite complex as the dyad is usually not enough information to frame a chord as minor or major in a way one might typically expect.
Listen to it now in the website visualizer: dyads.infinitedigits.co
Or on Bandcamp:
I worked from sheet music for the entire album. I wrote the score by hand but transcribed most of the pieces into Lilypond to archive as PDF.
https://editor.p5js.org/schollz/sketches/ZcdByv91a
let points = [];
let t = 0;
let gravityStrength = -0.3; // controls pull intensity
let center;
function setup() {
createCanvas(1800, 1800, P2D);
pixelDensity(displayDensity());
noFill();
smooth();
center = createVector(width / 2, height / 2);
let gridSize = 3;
let padL = 120;
let padR = 200;
let padT = 220;
let padB = 150;
let availableW = width - padL - padR;
let availableH = height - padT - padB;
let cellW = availableW / gridSize;
let cellH = availableH / gridSize;
for (let row = 0; row < gridSize; row++) {
for (let col = 0; col < gridSize; col++) {
let cx = padL + cellW * (col + 0.5);
let cy = padT + cellH * (row + 0.5);
let w = cellW * random(0.6, 0.7);
let h = cellH * random(0.6, 0.7);
let depth = random(0.25, 0.4);
let seed = int(random(1000));
let boxPts = createBox(cx, cy, w, h, depth, seed);
boxPts = smoothPoints(boxPts);
points = points.concat(boxPts);
}
}
}
function draw() {
background("#ffefbc");
stroke("#222222");
strokeWeight(14);
// apply gravitational pull
for (let p of points) {
let dir = p5.Vector.sub(center, p);
let dist = dir.mag();
dir.normalize();
// gravity falls off with distance
let pull = gravityStrength / (dist * 0.01 + 1);
p.add(dir.mult(pull));
}
beginShape();
if (points.length > 0) curveVertex(points[0].x, points[0].y);
for (let i = 1; i < t && i < points.length; i++) {
let p = points[i];
curveVertex(p.x, p.y);
}
if (t > 1 && t < points.length) {
let last = points[int(t)];
curveVertex(last.x, last.y);
}
endShape();
if (t < points.length) {
t += 1.5;
} else {
noLoop();
}
}
function createBox(cx, cy, w, h, depthFactor, seed) {
randomSeed(seed);
let d = w * depthFactor;
let angle = random(-PI / 6, PI / 6);
let cosA = cos(angle);
let sinA = sin(angle);
function rot(x, y) {
let dx = x - cx;
let dy = y - cy;
return createVector(
cx + dx * cosA - dy * sinA,
cy + dx * sinA + dy * cosA,
);
}
let fTL = rot(cx - w / 2, cy - h / 2);
let fTR = rot(cx + w / 2, cy - h / 2);
let fBR = rot(cx + w / 2, cy + h / 2);
let fBL = rot(cx - w / 2, cy + h / 2);
let bTL = rot(cx - w / 2 + d, cy - h / 2 - d);
let bTR = rot(cx + w / 2 + d, cy - h / 2 - d);
let bBR = rot(cx + w / 2 + d, cy + h / 2 - d);
let bBL = rot(cx - w / 2 + d, cy + h / 2 - d);
let ordered = [
fTL,
fTR,
fBR,
fBL,
fTL,
bTL,
bTR,
bBR,
bBL,
bTL,
bBL,
fBL,
fBR,
bBR,
bTR,
fTR,
];
let start = seed % ordered.length;
let rotated = [];
for (let i = 0; i < ordered.length; i++) {
rotated.push(ordered[(i + start) % ordered.length]);
}
return rotated;
}
function smoothPoints(arr) {
let smoothed = [];
for (let i = 0; i < arr.length - 1; i++) {
let p1 = arr[i];
let p2 = arr[i + 1];
smoothed.push(p1);
let mid = p5.Vector.lerp(p1, p2, 0.5);
smoothed.push(mid);
}
smoothed.push(arr[arr.length - 1]);
return smoothed;
}