add shapes animation
This commit is contained in:
parent
4b38f48f37
commit
db822f35c0
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/node_modules/
|
/node_modules/
|
||||||
|
tags
|
||||||
|
|
8
shell.nix
Normal file
8
shell.nix
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.python3
|
||||||
|
pkgs.nodejs
|
||||||
|
];
|
||||||
|
}
|
61
src/birds.js
Normal file
61
src/birds.js
Normal 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
47
src/birds.worker.js
Normal 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);
|
|
@ -3,10 +3,10 @@ import { getSpectrumData } from './util.js';
|
||||||
|
|
||||||
export default class CirclesAnimation {
|
export default class CirclesAnimation {
|
||||||
VOLUME_THRESHOLD = 20;
|
VOLUME_THRESHOLD = 20;
|
||||||
CIRCLE_RADIUS = 100;
|
CIRCLE_RADIUS = 30;
|
||||||
CIRCLE_POINTS = 256;
|
CIRCLE_POINTS = 256;
|
||||||
FFT_SIZE = 256;
|
FFT_SIZE = 256;
|
||||||
MAX_CIRCLES = 20;
|
MAX_CIRCLES = 50;
|
||||||
|
|
||||||
baseHeight;
|
baseHeight;
|
||||||
baseWidth;
|
baseWidth;
|
||||||
|
@ -15,7 +15,7 @@ export default class CirclesAnimation {
|
||||||
minPitch = 0;
|
minPitch = 0;
|
||||||
circles = [];
|
circles = [];
|
||||||
|
|
||||||
averageSpectrum;
|
averageSpectrum = [];
|
||||||
|
|
||||||
constructor(audioCtx) {
|
constructor(audioCtx) {
|
||||||
this.analyser = new AnalyserNode(audioCtx);
|
this.analyser = new AnalyserNode(audioCtx);
|
||||||
|
@ -83,11 +83,11 @@ export default class CirclesAnimation {
|
||||||
sketch.background(30);
|
sketch.background(30);
|
||||||
sketch.noFill();
|
sketch.noFill();
|
||||||
sketch.strokeWeight(2);
|
sketch.strokeWeight(2);
|
||||||
sketch.stroke(230);
|
|
||||||
|
|
||||||
for (let c = 0; c < this.circles.length; c++) {
|
for (let c = 0; c < this.circles.length; c++) {
|
||||||
const circle = this.circles[c];
|
const circle = this.circles[c];
|
||||||
sketch.beginShape();
|
sketch.beginShape();
|
||||||
|
sketch.stroke((c / this.circles.length) * 200 + 50);
|
||||||
for (let i = 0; i < circle.spectrum.length; i++) {
|
for (let i = 0; i < circle.spectrum.length; i++) {
|
||||||
const p = Math.max(circle.spectrum[i] - (this.averageSpectrum[i] * 0.6), 0);
|
const p = Math.max(circle.spectrum[i] - (this.averageSpectrum[i] * 0.6), 0);
|
||||||
sketch.curveVertex(
|
sketch.curveVertex(
|
||||||
|
|
16
src/index.js
16
src/index.js
|
@ -4,6 +4,8 @@ import './index.scss';
|
||||||
import HorizontalLinesAnimation from './horizontal-lines.js';
|
import HorizontalLinesAnimation from './horizontal-lines.js';
|
||||||
import SquaresAnimation from './squares.js';
|
import SquaresAnimation from './squares.js';
|
||||||
import CirclesAnimation from './circles.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 = 10 * 60 * 1000;
|
||||||
const ANIMATION_TIME = 20 * 1000;
|
const ANIMATION_TIME = 20 * 1000;
|
||||||
|
@ -16,11 +18,15 @@ async function main() {
|
||||||
const horizontalLines = new HorizontalLinesAnimation(audioCtx);
|
const horizontalLines = new HorizontalLinesAnimation(audioCtx);
|
||||||
const squares = new SquaresAnimation(audioCtx);
|
const squares = new SquaresAnimation(audioCtx);
|
||||||
const circles = new CirclesAnimation(audioCtx);
|
const circles = new CirclesAnimation(audioCtx);
|
||||||
|
const birds = new BirdsAnimation(audioCtx);
|
||||||
|
const shapes = new ShapesAnimation(audioCtx);
|
||||||
|
|
||||||
const animations = [
|
const animations = [
|
||||||
squares,
|
// birds,
|
||||||
horizontalLines,
|
shapes,
|
||||||
circles,
|
// circles,
|
||||||
|
// squares,
|
||||||
|
// horizontalLines,
|
||||||
];
|
];
|
||||||
|
|
||||||
let animationNum = 0;
|
let animationNum = 0;
|
||||||
|
@ -39,7 +45,9 @@ async function main() {
|
||||||
}
|
}
|
||||||
activeAnimation = animation;
|
activeAnimation = animation;
|
||||||
input.disconnect();
|
input.disconnect();
|
||||||
input.connect(activeAnimation.analyser);
|
if (activeAnimation.analyser) {
|
||||||
|
input.connect(activeAnimation.analyser);
|
||||||
|
}
|
||||||
animation.start(sketch);
|
animation.start(sketch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
121
src/shapes.js
Normal file
121
src/shapes.js
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -96,7 +96,7 @@ export default class SquaresAnimation {
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(sketch) {
|
draw(sketch) {
|
||||||
sketch.background('rgba(0, 0, 0, 0.02)');
|
sketch.background('rgba(0, 0, 0, 0.20)');
|
||||||
if (!this.fresh) {
|
if (!this.fresh) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ export default class SquaresAnimation {
|
||||||
const p = this.data.spectrum[i];
|
const p = this.data.spectrum[i];
|
||||||
sketch.stroke(color, 100, 100);
|
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 x = getRandomBetween(0, this.maxX) * this.GRID_SIZE;
|
||||||
const y = getRandomBetween(0, this.maxY) * this.GRID_SIZE;
|
const y = getRandomBetween(0, this.maxY) * this.GRID_SIZE;
|
||||||
sketch.beginShape();
|
sketch.beginShape();
|
||||||
|
|
Loading…
Reference in a new issue