A program developed using p5.js that generates album cover images resembling LP records. Each pattern is dynamically created based on the pitch and volume of the song, resulting in unique visuals for each track. The project demonstrates the transformation of sound into visual art through creative coding.
Vinyl Album Cover Generator | Visualizing Music with p5.js
var song;
var fft;
var amp;
let completed = [];
let numberOfCircles;
let points = [];
let backgroundColor = 0;
let playButton;
let currentTime;
function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSB);
background(backgroundColor);
// < Song List >
// song = loadSound("San Juan Sunset.mp3", finishedLoading);
// song = loadSound("Dexter Wansel - Theme from the Planets.mp3", finishedLoading);
// song = loadSound("Footin It.mp3", finishedLoading);
// song = loadSound("Seong Jin Cho Debussy Claire de lune.mp3", finishedLoading);
song = loadSound("Mariah Carey - All I Want For Christmas Is You.mp3", finishedLoading);
amp = new p5.Amplitude();
fft = new p5.FFT();
noFill();
playButton = createButton('Play');
playButton.position(20, 20);
playButton.mousePressed(togglePlay);
}
function togglePlay() {
if (song.isPlaying()) {
song.pause();
playButton.html('Play');
} else {
song.play();
playButton.html('Pause');
}
}
function finishedLoading() {
song.play();
fft.setInput(song);
numberOfCircles = Math.floor(song.duration() / 10) + 1;
for (let i = 0; i < numberOfCircles; i++) {
completed.push(false);
}
}
function isCloseEnough(a, b, tolerance = 0.1) {
return abs(a - b) < tolerance;
}
function addPoint(x, y, hue, dotSize) {
if (!points.some(point => isCloseEnough(point.x, x) && isCloseEnough(point.y, y))) {
points.push({ x, y, hue, dotSize });
}
}
function keyPressed() {
if (key === 's' || key === 'S') {
saveCanvas('myCanvas', 'png');
}
if (key === 'b' || key === 'B') {
backgroundColor = backgroundColor === 0 ? 255 : 0;
background(backgroundColor);
}
}
function draw() {
if (!song.isPlaying()) {
playButton.html('Play');
return;
} else {
playButton.html('Pause');
}
let currentPlayTime = song.currentTime();
let spectrum = fft.analyze();
let vol = amp.getLevel();
let dotSize = map(vol * 2, 0, 2, 0.01, 5);
let totalFrames = song.duration() / 10;
let startRadius = 5;
let radiusIncrease = 8;
for (let i = 0; i < numberOfCircles; i++) {
let radius = startRadius + i * radiusIncrease;
let currentFrame = (currentPlayTime / song.duration()) * totalFrames;
if (currentFrame > i && !completed[i]) {
let arcEnd = currentFrame < i + 1 ? map(currentFrame, i, i + 1, 0, TWO_PI) : TWO_PI;
for (let j = 0; j <= arcEnd; j += 0.02) {
let x = width / 2 + radius * cos(j - HALF_PI);
let y = height / 2 + radius * sin(j - HALF_PI);
let index = floor(j / 0.05) % spectrum.length;
let freqValue = spectrum[index];
let hue = map(freqValue, 0, 255, 0, 360);
addPoint(x, y, hue, dotSize);
}
if (currentFrame >= i + 1) {
completed[i] = true;
}
}
}
points.forEach(p => {
stroke(p.hue, 100, 100);
strokeWeight(p.dotSize);
point(p.x, p.y);
});
}
html,
body {
margin: 0;
padding: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- keep the line below for OpenProcessing compatibility -->
<script src="https://openprocessing.org/openprocessing_sketch.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/p5.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5@1.9.0/lib/addons/p5.sound.min.js"></script>
<script src="mySketch.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
</body>
</html>