xxxxxxxxxx
376
// require https://cdn.jsdelivr.net/npm/tweakpane@3.0.7/dist/tweakpane.min.js
// require https://cdn.jsdelivr.net/npm/p5@latest/lib/p5.min.js
// require https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.min.js
const pane = new Tweakpane.Pane();
// constants
const FR = 30;
const CANVAS_WIDTH = 900;
const CANVAS_HEIGHT = 600;
let mic = null;
let sphereID = 0;
const PARAMS = {
bgColor: "#FFF",
lineColor: "#FF7878",
// shape
diameter: 150,
margin: 2,
angle: 30,
circlePoints: 60,
strokeWeight: 0.3,
// anim
speed: 0.3,
seed: 315,
// diam noise
useDiameterNoise: true,
dNoiseAmplitude: 3.5,
dNoiseFrequency: 0.02,
// shape noise
useShapeNoise: true,
sNoiseAmplitude: 0.4,
sNoiseFrequency: 0.08,
// audio
showAudioGraph: true,
audioThresholdMin: 0.04,
audioThresholdMax: 1,
framePauseBetween: 20,
// room
roomHeight: 0.7,
roomMargin: 6,
};
const audioWaves = [];
let currentAudioMax = 0;
let isWave = false;
let framesSinceLastWave = 0;
let currentMaxVol = 0;
let isLooping = true;
function setup() {
createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
noFill();
frameRate(FR)
// add parameters
const f1 = pane.addFolder({
title: 'Base Shape',
expanded: false,
});
f1.addInput(PARAMS, "bgColor", {
view: 'color',
});
f1.addInput(PARAMS, "lineColor", {
view: 'color',
});
f1.addInput(PARAMS, "diameter", {
min: 0,
max: CANVAS_WIDTH,
});
f1.addInput(PARAMS, "margin", {
min: 2,
max: 100,
});
f1.addInput(PARAMS, "angle", {
min: 5,
max: 90,
});
f1.addInput(PARAMS, "circlePoints", {
step: 1,
min: 0,
max: 100,
});
f1.addInput(PARAMS, "strokeWeight", {
min: 0,
max: 2,
});
const f2 = pane.addFolder({
title: 'Animation',
expanded: false,
});
f2.addInput(PARAMS, "speed", {
min: 0,
max: 3,
});
f2.addInput(PARAMS, "seed", {
step: 1,
min: 0,
max: 999,
});
const f3 = pane.addFolder({
title: 'Diameter Noise',
expanded: false,
});
f3.addInput(PARAMS, "useDiameterNoise");
f3.addInput(PARAMS, "dNoiseAmplitude", {
min: 0,
max: 10,
});
f3.addInput(PARAMS, "dNoiseFrequency", {
step: 0.001,
min: 0,
max: 0.05,
});
const f4 = pane.addFolder({
title: 'Shape Noise',
expanded: false,
});
f4.addInput(PARAMS, "useShapeNoise");
f4.addInput(PARAMS, "sNoiseAmplitude", {
min: 0,
max: 1,
});
f4.addInput(PARAMS, "sNoiseFrequency", {
step: 0.001,
min: 0,
max: 0.1,
});
const f5 = pane.addFolder({
title: 'Audio Parameters',
expanded: false,
});
f5.addInput(PARAMS, "showAudioGraph");
f5.addInput(PARAMS, "audioThresholdMin", {
min: 0,
max: 1,
});
f5.addInput(PARAMS, "audioThresholdMax", {
min: 0,
max: 1,
});
f5.addInput(PARAMS, "framePauseBetween", {
step: 1,
min: 10,
max: 200,
});
const f6 = pane.addFolder({
title: 'Room',
expanded: false,
});
f6.addInput(PARAMS, "roomHeight", {
min: 0,
max: 1,
});
f6.addInput(PARAMS, "roomMargin", {
min: 2,
max: 50,
});
adjustValues();
pane.on("change", adjustValues);
mic = new p5.AudioIn();
mic.start();
audioWaves.push( new WobbleSphere() )
}
function adjustValues () {
strokeWeight(PARAMS.strokeWeight);
noiseSeed(PARAMS.seed);
}
function draw() {
background(PARAMS.bgColor);
stroke(PARAMS.lineColor);
const vol = mic.getLevel();
/*
if( vol > PARAMS.audioThresholdMin ){
if( !isWave ){
audioWaves.push( new WobbleSphere( vol ) );
}
framesSinceLastWave++;
isWave = true;
}else{
framesSinceLastWave = 0;
isWave = false;
}
if( framesSinceLastWave > PARAMS.framePauseBetween ){
framesSinceLastWave = 0;
isWave = false;
}
*/
if( vol > PARAMS.audioThresholdMin ){
framesSinceLastWave++;
isWave = true;
if( vol > currentMaxVol ){
currentMaxVol = vol;
}
}else if( isWave ){
audioWaves.push( new WobbleSphere( currentMaxVol, framesSinceLastWave ) );
framesSinceLastWave = 0;
isWave = false;
currentMaxVol = 0;
}
drawRoom();
audioWaves.forEach(w=>w.draw())
if(PARAMS.showAudioGraph){
if( vol > currentAudioMax ){
currentAudioMax = vol;
}
push();
if( vol > PARAMS.audioThresholdMin ){
fill(PARAMS.lineColor);
}else{
fill(100);
}
rect( 0,0,2, vol * height );
text(nfc(vol,2),8,12);
push();
fill(PARAMS.lineColor);
rect( 0,PARAMS.audioThresholdMin * height,6, 1 );
text(nfc(PARAMS.audioThresholdMin,2),8,PARAMS.audioThresholdMin * height);
rect( 0,PARAMS.audioThresholdMax * height,6, 1 );
text(nfc(PARAMS.audioThresholdMax,2),8,PARAMS.audioThresholdMax * height);
pop();
fill(0);
rect( 0,currentAudioMax * height,6, 1 );
text(nfc(currentAudioMax,2),8,currentAudioMax * height);
pop();
}
}
class WobbleSphere{
constructor( d = 0.1, length = 0 ){
// console.log(length)
this.cutCount = length
this.width = length * PARAMS.margin;
this.diameter = map(d,PARAMS.audioThresholdMin,PARAMS.audioThresholdMax,0,1,true) * PARAMS.diameter;
// global variables that are needed
// this.cutCount = int(this.width / PARAMS.margin);
this.sphere_counter = 0;
this.moveSpeed = 5;
this.posX = 0;
this.posY = random();
this.noiseShift = random();
this.ID = sphereID;
sphereID++;
}
draw(){
push();
translate( width - this.posX, 0 );
// console.log("draw",this.ID)
const tX = this.sphere_counter * PARAMS.speed;
const p_now = tX / PARAMS.margin;
if( tX > PARAMS.margin ){
this.sphere_counter = 0;
}
translate(0,height * this.posY);
for (let i = 0; i < this.cutCount; i++){
const p = lerp( i/this.cutCount, (i+1)/this.cutCount, p_now);
const sliceCut = lerp(0, this.width, p);
const radius = sqrt( this.width * sliceCut - pow(sliceCut,2) );
const h = this.diameter; //radius * 2;
const d = radius * 2 * cos(radians(90-PARAMS.angle));
translate( PARAMS.margin, 0 );
const dn = PARAMS.useDiameterNoise ? noise( this.noiseShift + PARAMS.dNoiseFrequency * frameCount, p ) * PARAMS.dNoiseAmplitude : 1;
// draw circle
beginShape();
for( let c = 0; c <= PARAMS.circlePoints; c++ ){
let x = sin( c/PARAMS.circlePoints * TWO_PI ) * d/2 * dn;
let y = cos( c/PARAMS.circlePoints * TWO_PI ) * h/2 * dn;
if( PARAMS.useShapeNoise ){
const n2 = sin( p * PI ) * PARAMS.sNoiseAmplitude * ( noise( frameCount * 0.01, PARAMS.sNoiseFrequency * c ) - 0.5 );
x += n2 * d;
y += n2 * h;
}
x += tX;
curveVertex( x, y );
}
endShape(CLOSE);
}
this.sphere_counter+=1;
this.posX += this.moveSpeed;
pop();
// delete sphere again if out of frame
if( this.posX > width + this.width * 2 ){
this.destroy();
}
}
destroy(){
const arrayIndex = audioWaves.findIndex( w => w.ID === this.ID );
delete audioWaves[arrayIndex];
audioWaves.splice(arrayIndex, 1);
}
}
function drawRoom() {
const horizon = height * PARAMS.roomHeight;
const floorLineShift = (height-horizon) * -1 / sin(radians(PARAMS.angle));
const roomLines = ( width + abs(floorLineShift) ) / PARAMS.roomMargin;
push();
// vertical
for( let i = 0; i < roomLines; i++ ){
translate(PARAMS.roomMargin,0)
if( i * PARAMS.roomMargin < width ){
line(0,0,0,horizon)
}
line(0,horizon,floorLineShift,height);
}
pop();
}
function keyPressed() {
if( key === "p" || key === "P" ){
isLooping ? noLoop() : loop();
isLooping = !isLooping;
}
}