Dancers

Silly characters listen and react as you play music. This sketch was tuned for violin but may work with other instruments, or your voice!

This sketch needs permission to use your microphone.

Source Code for this Sketch

// activationThreshold is the energy required for
// the dancers to dance in response to any given
// note.
//
// Depending on your microphone, background noise,
// type of instrument, or volume of your voice,
// you may need to adjust this value.
const activationThreshold = 255*.35

// You can use this multiplier value to make the
// dancers more energetic. A value of 2 will make
// them dance twice as quickly, for example.
const dancerSpeed = 1

const eNote = 660
const aNote = 440
const dNote = 293.33333
const gNote = 195.55555

const height = 200
const topOffset = 10;
const bottomOffset = height-10;

let fft = new p5.FFT()
let averages = []
let count = 0
let hue = 0

class Wiggler {
    constructor(range, speed) {
        this.range = range ? range : 30
        this.speed = speed ? speed/100 : 40/100
        this._x = 0
        this.x = 0
    }

    update() {
        this._x += PI*this.speed
        this.x = sin(this._x) * this.range
    }
}

let eos = new Wiggler(20, 10 * dancerSpeed)
let aos = new Wiggler(30, 6 * dancerSpeed)
let dos = new Wiggler(40, 6 * dancerSpeed)
let gos = new Wiggler(25, 8 * dancerSpeed)

function eye(x, y) {
    fill(100)
    circle(x, y, 12)
    fill(0)
    circle(x, y, 5)
}

function setup() {
    createCanvas(600, height, P2D, canvas)
    colorMode(HSL)

    mic = new p5.AudioIn()
    mic.start()

    fft.setInput(mic)

    for (let i = 0; i < 1024; i++) {
        averages[i] = [0, 0, 0, 0, 0]
    }
}

function draw() {
    background(100)
    fft.analyze()

    // Instruments don't produce pure tones so
    // you may see other dancers participating
    // even if they are not the primary note.
    // And for some reason, A likes to join in
    // with G, even when a pure tone is used.
    // Maybe it's harmonics, maybe it's a bug.

    // E note
    if (fft.getEnergy(eNote/2) > activationThreshold ||
        fft.getEnergy(eNote) > activationThreshold ||
        fft.getEnergy(eNote*2) > activationThreshold) {
        eos.update()
    }
    fill(0, 100, 50)
    beginShape()
        vertex(500, topOffset)
        bezierVertex(490+eos.x, 60, 490+eos.x, 60, 500, bottomOffset)
        bezierVertex( 510+eos.x, 140, 510+eos.x, 140, 500, topOffset)
    endShape()
    eye(480, 50)
    eye(520, 50)

    // A note
    if (fft.getEnergy(aNote/2) > activationThreshold ||
        fft.getEnergy(aNote) > activationThreshold ||
        fft.getEnergy(aNote*2) > activationThreshold) {
        aos.update()
    }
    fill(50, 100, 50)
    beginShape()
        vertex(400, topOffset)
        bezierVertex(400, topOffset+50, 350-aos.x, bottomOffset, 400, bottomOffset)
        bezierVertex( 450+aos.x, bottomOffset, 400, topOffset+50, 400, topOffset)
    endShape()
    eye(390, bottomOffset-20)
    eye(410, bottomOffset-20)

    // D note
    if (fft.getEnergy(dNote) > activationThreshold ||
        fft.getEnergy(dNote*2) > activationThreshold ||
        fft.getEnergy(dNote*4) > activationThreshold) {
        dos.update()
    }
    fill(110, 81, 40)
    beginShape()
        vertex(300, topOffset)
        // Curve handles are pulled 20px toward their primary axes
        // Left side
        bezierVertex(300, topOffset+20, 275, topOffset+45+dos.x, 255, topOffset+45+dos.x)
        bezierVertex(300, topOffset+45+dos.x, 300, bottomOffset-45-dos.x, 255, bottomOffset-45-dos.x)
        bezierVertex(275, bottomOffset-45-dos.x, 300, bottomOffset-20, 300, bottomOffset)
        // Right side
        bezierVertex(300, bottomOffset-20, 325, bottomOffset-45-dos.x, 345, bottomOffset-45-dos.x)
        bezierVertex(300, bottomOffset-45-dos.x, 300, topOffset+45+dos.x, 345, topOffset+45+dos.x)
        bezierVertex(325, topOffset+45+dos.x, 300, topOffset+20, 300, topOffset)
    endShape()
    eye(290, 100)
    eye(310, 100)

    // G note
    if (fft.getEnergy(gNote) > activationThreshold ||
        fft.getEnergy(gNote*2) > activationThreshold ||
        fft.getEnergy(gNote*4) > activationThreshold) {
        gos.update()
    }
    const midpoint = 130
    fill(237, 53, 40)
    beginShape()
        vertex(150, topOffset)
        bezierVertex(100-gos.x, topOffset+40, 100-gos.x, midpoint-40, 140, midpoint)
        bezierVertex(100-gos.x, midpoint+20, 100-gos.x, bottomOffset-20, 150, bottomOffset)
        bezierVertex(200+gos.x, bottomOffset-20, 200+gos.x, midpoint+20, 160, midpoint)
        bezierVertex( 200+gos.x, midpoint-40, 200+gos.x, topOffset+40, 150, topOffset)
    endShape()
    eye(140, midpoint)
    eye(160, midpoint)
}

You may use all or part of the p5 code seen on this page for your own creations, without restriction. Attribution is appreciated but not required.

This sketch also includes the following sources: p5common.js p5.sound.js . Sources are available under their respective licenses.

See all p5.js sketches