add shapes animation

This commit is contained in:
Benjamin Bädorf 2021-09-17 20:43:42 +02:00
parent 4b38f48f37
commit db822f35c0
No known key found for this signature in database
GPG key ID: 4406E80E13CD656C
9 changed files with 257 additions and 10 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
eval "$(lorri direnv)"

1
.gitignore vendored
View file

@ -1 +1,2 @@
/node_modules/
tags

8
shell.nix Normal file
View file

@ -0,0 +1,8 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = [
pkgs.python3
pkgs.nodejs
];
}

61
src/birds.js Normal file
View file

@ -0,0 +1,61 @@
import { getSpectrumData, getRandomBetween } from './util.js';
const BIRD_NUM = 300;
export default class BirdsAnimation {
maxX;
maxY;
birds = [];
constructor() { }
start(sketch) {
this.resize();
this.worker = new Worker();
this.worker.onmessage = e => this.onBirdPositions(e.data);
this.worker.postMessage({
maxX: this.maxX,
maxY: this.maxY,
});
sketch.background(0);
sketch.colorMode(sketch.HSB);
}
onBirdPositions(data) {
this.birds = data.birds;
}
resize() {
this.maxX = window.innerWidth;
this.maxY = window.innerHeight;
}
stop() {
this.worker.terminate();
}
draw(sketch) {
sketch.background('rgba(0, 0, 0, 0.20)');
const brightness = ((this.data.volume - this.minVolume) / (this.maxVolume - this.minVolume)) * 80;
const color = ((this.data.pitch - this.minPitch) / (this.maxPitch - this.minPitch)) * 360;
for (let i = 0; i < brightness; i++) {
const p = this.data.spectrum[i];
sketch.stroke(color, 100, 100);
sketch.fill(color, 100, brightness);
const x = getRandomBetween(0, this.maxX) * this.GRID_SIZE;
const y = getRandomBetween(0, this.maxY) * this.GRID_SIZE;
sketch.beginShape();
sketch.vertex(x, y);
sketch.vertex(x, y + this.GRID_SIZE);
sketch.vertex(x + this.GRID_SIZE, y + this.GRID_SIZE);
sketch.vertex(x + this.GRID_SIZE, y);
sketch.vertex(x, y);
sketch.endShape();
}
}
}

47
src/birds.worker.js Normal file
View file

@ -0,0 +1,47 @@
import Worker from './birds.worker.js';
const STEP_TIME = 10;
const
const data = {};
function onInitialData(event) {
data.maxX = event.maxX;
data.maxY = event.maxY;
for (let i = 0; i < BIRD_NUM; i += 1) {
data.birds.push({
x: getRandomBetween(0, data.maxX),
y: getRandomBetween(0, data.maxY),
r: getRandomBetween(0, 360),
v: getRandomBetween(1, 10),
});
}
}
self.onmessage = onInitialData;
function calculateAndSend() {
const {
birds,
maxX,
maxY,
} = data;
for (const bird of birds) {
}
self.postMessage({
minVolume,
averageVolume,
maxVolume,
minPitch,
averagePitch,
maxPitch,
averageSpectrum,
});
}
setInterval(calculateAndSend, STEP_TIME);

View file

@ -3,10 +3,10 @@ import { getSpectrumData } from './util.js';
export default class CirclesAnimation {
VOLUME_THRESHOLD = 20;
CIRCLE_RADIUS = 100;
CIRCLE_RADIUS = 30;
CIRCLE_POINTS = 256;
FFT_SIZE = 256;
MAX_CIRCLES = 20;
MAX_CIRCLES = 50;
baseHeight;
baseWidth;
@ -15,7 +15,7 @@ export default class CirclesAnimation {
minPitch = 0;
circles = [];
averageSpectrum;
averageSpectrum = [];
constructor(audioCtx) {
this.analyser = new AnalyserNode(audioCtx);
@ -83,11 +83,11 @@ export default class CirclesAnimation {
sketch.background(30);
sketch.noFill();
sketch.strokeWeight(2);
sketch.stroke(230);
for (let c = 0; c < this.circles.length; c++) {
const circle = this.circles[c];
sketch.beginShape();
sketch.stroke((c / this.circles.length) * 200 + 50);
for (let i = 0; i < circle.spectrum.length; i++) {
const p = Math.max(circle.spectrum[i] - (this.averageSpectrum[i] * 0.6), 0);
sketch.curveVertex(

View file

@ -4,6 +4,8 @@ import './index.scss';
import HorizontalLinesAnimation from './horizontal-lines.js';
import SquaresAnimation from './squares.js';
import CirclesAnimation from './circles.js';
import BirdsAnimation from './birds.js';
import ShapesAnimation from './shapes.js';
// const ANIMATION_TIME = 10 * 60 * 1000;
const ANIMATION_TIME = 20 * 1000;
@ -16,11 +18,15 @@ async function main() {
const horizontalLines = new HorizontalLinesAnimation(audioCtx);
const squares = new SquaresAnimation(audioCtx);
const circles = new CirclesAnimation(audioCtx);
const birds = new BirdsAnimation(audioCtx);
const shapes = new ShapesAnimation(audioCtx);
const animations = [
squares,
horizontalLines,
circles,
// birds,
shapes,
// circles,
// squares,
// horizontalLines,
];
let animationNum = 0;
@ -39,7 +45,9 @@ async function main() {
}
activeAnimation = animation;
input.disconnect();
input.connect(activeAnimation.analyser);
if (activeAnimation.analyser) {
input.connect(activeAnimation.analyser);
}
animation.start(sketch);
}

121
src/shapes.js Normal file
View file

@ -0,0 +1,121 @@
import p5 from 'p5';
import { getSpectrumData, getRandomBetween } from './util.js';
const MIN_SIDES = 3;
const MAX_SIDES = 6;
const SHAPE_NUM = 12;
export default class ShapesAnimation {
FFT_SIZE = 1024;
maxX;
maxY;
minVolume = 360;
maxVolume = 0;
shapes = [];
data;
fresh = false;
analyser;
audioProcessor
constructor(audioCtx) {
this.analyser = new AnalyserNode(audioCtx);
this.analyser.fftSize = this.FFT_SIZE;
this.analyser.timeSmoothingConstant = 0.2;
this.audioProcessor = audioCtx.createScriptProcessor(this.FFT_SIZE * 2, 1, 1);
this.audioProcessor.onaudioprocess = () => this.audioProcess();
}
start(sketch) {
this.resize();
this.analyser.connect(this.audioProcessor);
this.shapes = (new Array(SHAPE_NUM))
.fill(null)
.map(() => {
const baseX = getRandomBetween(0, this.maxX);
const baseY = getRandomBetween(0, this.maxY);
const baseV = sketch.createVector(
getRandomBetween(-5, 5),
getRandomBetween(-5, 5),
getRandomBetween(-5, 5),
);
return (new Array(getRandomBetween(MIN_SIDES, MAX_SIDES)))
.fill(null)
.map(() => ({
x: getRandomBetween(baseX - 50, baseX + 50),
y: getRandomBetween(baseY - 50, baseY + 50),
v: sketch.createVector(
getRandomBetween(-10, 10) / 3,
getRandomBetween(-10, 10) / 3,
getRandomBetween(-10, 10) / 3,
).add(baseV),
}));
});
sketch.background(0);
sketch.colorMode(sketch.HSB);
}
stop() {
this.analyser.disconnect();
}
resize() {
this.maxX = window.innerWidth;
this.maxY = window.innerHeight;
}
getAverageData(data) {
this.minVolume = data.minVolume;
this.maxVolume = data.maxVolume;
this.minPitch = data.minPitch;
this.maxPitch = data.maxPitch;
this.averageSpectrum = data.averageSpectrum;
}
audioProcess() {
const spectrum = new Uint8Array(this.analyser.frequencyBinCount);
this.analyser.getByteFrequencyData(spectrum);
spectrum.reverse();
const data = getSpectrumData(spectrum);
const { volume, pitch } = data;
if (volume > this.maxVolume) {
this.maxVolume = volume;
}
if (volume !== 0 && volume < this.minVolume) {
this.minVolume = volume;
}
this.data = data;
this.fresh = true;
}
draw(sketch) {
sketch.background(0, 0, 10);
sketch.stroke(0, 0, 255);
sketch.fill(0, 0, 255);
for (const shape of this.shapes) {
sketch.beginShape();
for (const point of shape) {
sketch.vertex(point.x, point.y);
point.x = point.x + point.v.x;
point.y = point.y + point.v.y;
point.z = point.z + point.v.z;
}
sketch.endShape();
}
}
}

View file

@ -96,7 +96,7 @@ export default class SquaresAnimation {
}
draw(sketch) {
sketch.background('rgba(0, 0, 0, 0.02)');
sketch.background('rgba(0, 0, 0, 0.20)');
if (!this.fresh) {
return;
}
@ -107,7 +107,7 @@ export default class SquaresAnimation {
const p = this.data.spectrum[i];
sketch.stroke(color, 100, 100);
sketch.fill(color, 100, brightness);
sketch.fill(color, 100, 100);
const x = getRandomBetween(0, this.maxX) * this.GRID_SIZE;
const y = getRandomBetween(0, this.maxY) * this.GRID_SIZE;
sketch.beginShape();