LOADING...

Preview

Pen ID
Unlock Campus Themeforest adv

 

Code
CSS
//-------------------------
// QUICK KEY
//-------------------------
// TRIANGLE = Registered User
// CIRCLE = Anonymouse User
// SQUARE = Bot
//-------------------------
// BLUE = Addition
// RED = Deletion
//-------------------------
// WASHOUT = New User
//-------------------------
JS
// Be warned... the audio glitches...
// and everything could be more performant.
// I did what I could for a saturday morning :-/

/**************************
* Initialize array for
* shapes state
**************************/
const shapes = [];

/**************************
* Audio handling
**************************/
class Windchime {
  constructor() {
    // Cycling Chord Number
    this.chordNo = 1;
    
    // Tonal references
    this.pitches = [
      146, 165, 183, 195, 220, 244, 275, 293, 330, 367,
      391, 440, 489, 550, 587, 660, 734, 783, 880, 978,
      1101, 1174, 1321, 1468, 1566
    ];
    this.fifths = [
      [4,9], 
      [2,6], 
      [5,10]
    ]
    this.chords = [
      [5, 7, 11, 13, 14, 16, 18, 20, 22, 23],
      [6, 8, 9, 11, 13, 15, 16, 18, 20, 22],
      [5, 7, 8, 10, 12, 14, 15, 17, 19, 21]
    ];
    
    // New user pad/drone settings
    this.chordEnv = [0, [0.3, 1000], [0.3, 3000], [0, 5000]];
    this.chordFiltEnv = [700, [1200, 1500], [1500, 2000], [1000, 2500], [1000, 5000]];
    this.chordCutoff = T('env', {table:this.chordFiltEnv}).bang();
    
    this.chordChange = this.chordChange.bind(this);
    this.chordChange();
  }
  
  chordChange() {
    this.chordNo = this.chordNo === 2 ? 0 : this.chordNo + 1
    setTimeout(this.chordChange, 8000);
  }
  
  soundWikiChange(addition) {
    const chordNo = this.chordNo;
    const chords = this.chords;
    const pitches = this.pitches;

    let sound = T(addition ? 'sin' : 'saw', {freq:pitches[chords[chordNo][parseInt(random(0, 9))]], mul:0.09});
    sound = addition ? sound : T('lpf' , {cutoff: 670, Q: 12}, sound);
    sound = T('reverb', {room:2, damp:0.1, mix:0.7}, sound);
    
    T('perc', {r: 2500}, sound)
      .on('ended', function() {
        this.pause();
      })
      .bang()
      .play();
  }
  
  soundNewUser() {
    const chordCutoff = this.chordCutoff;
    const chordEnv = this.chordEnv;
    const chordNo = this.chordNo;
    const fifths = this.fifths;
    const pitches = this.pitches;
    
    let sound = T(
      'lpf',
      {cutoff:chordCutoff, Q:3},
      T('sin', {freq: pitches[fifths[chordNo][0]], mul:0.05}),
      T('sin', {freq: pitches[fifths[chordNo][1]], mul:0.05}),
      T('pink', {mul:0.1})
    );
    sound = T('reverb', {room:2, damp:0.1, mix:0.7}, sound);
  
    T('env', {table:chordEnv}, sound)
      .on('ended', function() {
        this.pause();
      })
      .bang()
      .play();
  }
};
const windchime = new Windchime();

/**************************
* New user handling
**************************/
class NewUser {
  constructor() {
    this.timeout;
    this.triggered = false;
    this.mod = 10;
  }
  
  bang() {
    this.triggered = true;
    clearTimeout(this.timeout);
    this.timeout = setTimeout(() => {this.triggered = false}, 3000);
  
    windchime.soundNewUser();
  }
  
  update() {
    if (this.triggered && this.mod < 100) {
      this.mod += 2;
    } else if (this.mod > 10) {
      this.mod -= 0.5;
    }
  }
}
const newUser = new NewUser();

/**************************
* Get polygon points
**************************/
function getPolygon(x, y, size, rot = 0, sides = 3) {
  return Array(sides)
    .fill()
    .map((v, i) => i * 2 / sides * Math.PI + rot)
    .reduce((a, v, i) => (a.concat([
      size / 2 * Math.cos(v) + x,
      size / 2 * Math.sin(v) + y
    ])), []);
}

/**************************
* Utility to temper sizes
**************************/
function dimval(n, min_in, max_in, min_out, max_out, exponent) {
  n -= min_in
  n /= max_in - min_in
  n = Math.pow(n, exponent)
  n *= max_out - min_out
  n += min_out
  return n
}

/**************************
* Connect to SSE stream
**************************/
const es = new EventSource('https://wikimon.now.sh');
es.onmessage = e => {
  processData(
    JSON.parse(e.data)
  );
};

/**************************
* Handle data from stream
**************************/
function processData(message) {
  // New user
  if (message.action === 'create' && message.summary === 'New user account') {
    newUser.bang();
  }
  
  // Add to shapes state
  if (!document[hidden]) {
    shapes.unshift({
      addition: message.change_size > 0,
      size: dimval(
        Math.abs(message.change_size),
        0, 100000, 20, 4000, 0.5),
      title: message.page_title,
      anon: message.is_anon,
      bot: message.is_bot,
      x: random(windowWidth),
      y: random(windowHeight),
      speedRot: random(-1.3, 1.3),
      speedX: random(-2, 2),
      speedY: random(-2, 2),
      life: 120
    });
  }
  
  windchime.soundWikiChange(message.change_size > 0);
}

/**************************
* p5 fun
**************************/
function setup() {
  createCanvas(windowWidth, windowHeight);
  colorMode(RGB, 120);
  textFont('Monospace');
  textSize(12);
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  colorMode(RGB, 120);
}

function draw() {
  /*****************************
  * Clear the canvas
  * Brighten/decrease opacity
  * based on newUserMod
  *****************************/
  background(
    16 + newUser.mod / 7,
    20 + newUser.mod / 5,
    26 + newUser.mod / 4,
    120 - newUser.mod
  );

  shapes.forEach((shape, i, shapes) => {
    /**********************
    * Setup shape
    **********************/
    fill(120,0); // No fill

    // Determine color
    if (shape.addition) {
      stroke(20, 120, 120, shape.life); // addition: blue
    } else {
      stroke(120, 50, 50, shape.life); // removal: red
    }
    
    // Draw shape
    if (shape.bot) {
      quad(...getPolygon( // bot: square
        shape.x,
        shape.y,
        shape.size,
        (radians(frameCount) * shape.speedRot),
        4
      ));
    } else if (shape.anon) {
      ellipse(shape.x, // anon: circle
        shape.y,
        shape.size,
        shape.size
      );
    } else {
      triangle(...getPolygon( // user: triangle
        shape.x,
        shape.y,
        shape.size,
        (radians(frameCount) * shape.speedRot)
      ));
    }
    
    /**********************
    * Setup text
    **********************/
    stroke(0,0,0,0); // No stroke
    
    // Determine color
    if (shape.addition) {
      fill(20, 120, 120, shape.life); // addition: blue
    } else {
      fill(120, 50, 50, shape.life); // removal: red
    }
    
    // Font family and size
    textFont('Monospace');
    textSize(12);
    
    // Position and draw title text
    text(shape.title, shape.x, shape.y + 4);
    
    /**********************
    * Mutate/update state
    **********************/
    // Drift
    shape.x = shape.x + shape.speedX;
    shape.y = shape.y + shape.speedY;
    
    // Remove shape if life is over
    // Age if still alive
    if (shape.life < 1) {
      shapes.splice(i, 1);
    } else {
      shape.life = shape.life - 1;
    }
  });
  
  newUser.update();

  // Setup title text
  fill(110, 120);
  stroke(0, 0);
  textSize(27);
  
  // Draw title text
  text('WIKIPEDIA AUDIOVISUALIZER', windowWidth - windowWidth / 2 - 205, 30);
}

document.body.style.overflow = 'hidden';

Description

UPDATE: Codepen recently switched to https, but the websocket that this uses isn't currently able to work over wss. I hacked together a quick Event Stream that approximates the websocket for now and threw it up on Now.
Term
Mon, 11/27/2017 - 21:27

Related Codes

Pen ID
Pen ID
Pen ID
Square Adv