|
// Be warned... the audio glitches... |
|
// and everything could be more performant. |
|
// I did what I could for a saturday morning :-/ |
|
|
|
// Global Chord Cycle |
|
var chordNo = 1; |
|
|
|
// Cycle Global Chord |
|
function chordChange() { |
|
if (chordNo === 2){ |
|
chordNo = 0 |
|
} else { |
|
chordNo++ |
|
} |
|
setTimeout(chordChange, 8000); |
|
} |
|
chordChange(); |
|
|
|
// Tonal References |
|
var 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]; |
|
var fifths = [ |
|
[4,9], |
|
[2,6], |
|
[5,10] |
|
] |
|
var 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 |
|
var chordEnv = [0, [0.3, 1000], [0.3, 3000], [0, 5000]]; |
|
var chordFiltEnv = [700, [1200, 1500], [1500, 2000], [1000, 2500], [1000, 5000]]; |
|
var chordCutoff = T("env", {table:chordFiltEnv}).bang(); |
|
|
|
// 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 |
|
} |
|
|
|
/*------------------------------------- |
|
OLD - CODEPEN SWITCHED TO HTTPS AND |
|
ADD THIS WEBSOCKET DOESN'T CURRENTLY |
|
SUPPORT HTTPS: |
|
//------------------------------------*/ |
|
// Connect to wikipedia websocket |
|
// var ws = new ReconnectingWebSocket('ws://wikimon.hatnote.com/en/'); |
|
|
|
// ws.onmessage = function(event) { |
|
// var dat = JSON.parse(event.data); |
|
// processData(dat); |
|
// }; |
|
|
|
/*------------------------------------- |
|
NEW - I CREATED AN EVENT STREAM MUCH |
|
LIKE THE WEBSOCKET AND HOSTED IT ON NOW |
|
//------------------------------------*/ |
|
var es = new EventSource("https://wikimon.now.sh"); |
|
es.onmessage = function(e) { |
|
var dat = JSON.parse(e.data); |
|
processData(dat); |
|
}; |
|
|
|
var shapes = []; |
|
function processData(message) { |
|
var shape = {}; |
|
// New user |
|
if (message.action === "create" && message.summary === "New user account") { |
|
newUser(); |
|
} |
|
|
|
// Generate shape data |
|
if (message.change_size > 0) { |
|
shape.addition = true; |
|
} else { |
|
shape.addition = false; |
|
} |
|
var _size = Math.abs(message.change_size); |
|
shape.size = dimval(_size, 0, 100000, 20, 4000, 0.5); |
|
shape.title = message.page_title; |
|
shape.anon = message.is_anon; |
|
shape.bot = message.is_bot; |
|
shape.x = random(windowWidth); |
|
shape.y = random(windowHeight); |
|
shape.speedRot = random(-1.3, 1.3); |
|
shape.speedX = random(-2, 2); |
|
shape.speedY = random(-2, 2); |
|
shape.life = 120; |
|
if (!document[hidden]) { |
|
shapes.unshift(shape); |
|
} |
|
|
|
// Audio |
|
if (!shape.addition) { |
|
var pitch = T("saw", {freq:pitches[chords[chordNo][parseInt(random(0, 9))]], mul:0.09}); |
|
var synth = T("lpf" , {cutoff:670, Q:12}, pitch); |
|
} else { |
|
var synth = T("sin", {freq:pitches[chords[chordNo][parseInt(random(0, 9))]], mul:0.09}); |
|
} |
|
synth = T("reverb", {room:2, damp:0.1, mix:0.7}, synth); |
|
T("perc", {r:2500}, synth).on("ended", function() { |
|
this.pause(); |
|
}).bang().play(); |
|
} |
|
|
|
var userTimeout; |
|
var isNewUser = false; |
|
var newUserMod = 10; |
|
function newUser() { |
|
isNewUser = true; |
|
clearTimeout(userTimeout); |
|
userTimeout = setTimeout(function(){ |
|
isNewUser = false; |
|
},3000); |
|
|
|
// Audio |
|
var _1 = 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})); |
|
var _1 = T("reverb", {room:2, damp:0.1, mix:0.7}, _1) |
|
T("env", {table:chordEnv},_1).on("ended", function() { |
|
this.pause(); |
|
}).bang().play(); |
|
} |
|
|
|
// Create rotation around a center point |
|
function rotatePoint(pX, pY, cX, cY, rot) { |
|
x = cX + (pX-cX)*Math.cos(rot) - (pY-cY)*Math.sin(rot); |
|
y = cY + (pX-cX)*Math.sin(rot) + (pY-cY)*Math.cos(rot); |
|
return { |
|
x: x, |
|
y: y |
|
}; |
|
} |
|
|
|
// Get triangle points from center point, size, rotation |
|
function getTri(x,y,size,rot) { |
|
var angles = [0,(2/3*Math.PI),(4/3*Math.PI)] |
|
var points = {}; |
|
var r = size/2; |
|
var _x,_y,_calc; |
|
for (var i = 0; i < angles.length; i++) { |
|
_x = r*cos(angles[i]) + x; |
|
_y = r*sin(angles[i]) + y; |
|
_calc = rotatePoint(_x, _y, x, y, rot); |
|
points['x'+(i+1)] = _calc.x; |
|
points['y'+(i+1)] = _calc.y; |
|
} |
|
return points; |
|
} |
|
|
|
// Get square points from center point, size, rotation |
|
// Could be writted better, but its the weekend, |
|
// and my brain is lazy. |
|
function getSquare(x, y, size, rot) { |
|
var _size = size/2; |
|
var points = []; |
|
var x1 = x - _size; |
|
var y1 = y - _size; |
|
var x2 = x + _size; |
|
var y2 = y - _size; |
|
var x3 = x + _size; |
|
var y3 = y + _size; |
|
var x4 = x - _size; |
|
var y4 = y + _size; |
|
points.push(rotatePoint(x1, y1, x, y, rot)); |
|
points.push(rotatePoint(x2, y2, x, y, rot)); |
|
points.push(rotatePoint(x3, y3, x, y, rot)); |
|
points.push(rotatePoint(x4, y4, x, y, rot)); |
|
return points; |
|
} |
|
|
|
// Setup p5 |
|
function setup() { |
|
createCanvas(windowWidth, windowHeight); |
|
colorMode(RGB, 120); |
|
textFont("Monospace"); |
|
textSize(12); |
|
} |
|
function windowResized() { |
|
resizeCanvas(windowWidth, windowHeight); |
|
colorMode(RGB, 120); |
|
} |
|
|
|
function draw() { |
|
// Clear the slate |
|
background(16+(newUserMod/7),20+(newUserMod/5),26+(newUserMod/4),120-newUserMod); |
|
|
|
for (var i = 0; i < shapes.length; i++) { |
|
// No fill |
|
fill(120,0); |
|
// If addition: blue, is removal: red |
|
if (shapes[i].addition) { |
|
stroke(20, 120, 120, shapes[i].life); |
|
} else { |
|
stroke(120, 50, 50, shapes[i].life); |
|
} |
|
|
|
// Square if bot |
|
if (shapes[i].bot) { |
|
var _sq = getSquare( |
|
shapes[i].x, |
|
shapes[i].y, |
|
shapes[i].size, |
|
(radians(frameCount)*shapes[i].speedRot) |
|
); |
|
quad(_sq[0].x,_sq[0].y, |
|
_sq[1].x,_sq[1].y, |
|
_sq[2].x,_sq[2].y, |
|
_sq[3].x,_sq[3].y); |
|
} else |
|
|
|
// Circle if anon |
|
if (shapes[i].anon) { |
|
ellipse(shapes[i].x, |
|
shapes[i].y, |
|
shapes[i].size, |
|
shapes[i].size); |
|
} else |
|
|
|
// Triangle if friendly neighborhood user |
|
{ |
|
var _tri = getTri( |
|
shapes[i].x, |
|
shapes[i].y, |
|
shapes[i].size, |
|
(radians(frameCount)*shapes[i].speedRot) |
|
); |
|
triangle(_tri.x1, _tri.y1, |
|
_tri.x2, _tri.y2, |
|
_tri.x3, _tri.y3); |
|
} |
|
stroke(0,0,0,0); |
|
if (shapes[i].addition) { |
|
fill(20, 120, 120, shapes[i].life); |
|
} else { |
|
fill(120, 50, 50, shapes[i].life); |
|
} |
|
textFont("Monospace"); |
|
textSize(12); |
|
text(shapes[i].title, shapes[i].x, shapes[i].y+4); |
|
|
|
// Drift |
|
shapes[i].x = shapes[i].x + shapes[i].speedX; |
|
shapes[i].y = shapes[i].y + shapes[i].speedY; |
|
|
|
// Remove shape if life is over |
|
// Age if still alive |
|
if (shapes[i].life < 1) { |
|
shapes.splice(i, 1); |
|
} else { |
|
shapes[i].life = shapes[i].life - 1; |
|
} |
|
} |
|
if (isNewUser) { |
|
if (newUserMod < 100) { |
|
newUserMod = newUserMod+2; |
|
} |
|
} else { |
|
if (newUserMod > 10) { |
|
newUserMod = newUserMod-0.5; |
|
} |
|
} |
|
fill(110,120); |
|
stroke(0,0); |
|
textSize(27); |
|
text("WIKIPEDIA AUDIOVISUALIZER", windowWidth-windowWidth/2-205, 30); |
|
} |
|
|
|
document.body.style.overflow = "hidden"; |
Original by https://codepen.io/halvves/