Created
October 10, 2013 17:21
-
-
Save jgv/6922177 to your computer and use it in GitHub Desktop.
webcam synth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.onload = function(){ | |
navigator.getMedia = (navigator.getUserMedia || | |
navigator.webkitGetUserMedia || | |
navigator.mozGetUserMedia || | |
navigator.msGetUserMedia); | |
(function init(g){ | |
try { | |
Synth.audio.ctx = new (g.AudioContext || g.webkitAudioContext); | |
Synth.audio.oscillator = Synth.audio.ctx.createOscillator(); | |
Synth.init() | |
} catch (e) { | |
alert('No web audio oscillator support in this browser. Please upgrade to latest Chrome.'); | |
} | |
}(window)); | |
}; | |
var Synth = (Synth || {}); | |
Synth = { | |
'audio': { | |
'ctx': null, | |
'oscillator': null | |
}, | |
'video': document.getElementById('video'), | |
'canvas': document.getElementById('canvas'), | |
'ctx': this.canvas.getContext('2d'), | |
'oscs': [], | |
'maxOscs': 9, | |
'colorShiftThreshold': 1, | |
'multiplier': 2.8431372549019605, // 7.8431372549019605 | |
'camera': { | |
'init': function(){ | |
navigator.webkitGetUserMedia( | |
{ | |
audio:false, | |
video:true | |
}, | |
// access granted | |
function(stream){ | |
if (navigator.mozGetUserMedia) { | |
Synth.video.mozSrcObject = stream; | |
} else { | |
var vendorURL = window.URL || window.webkitURL; | |
Synth.video.src = vendorURL.createObjectURL(stream) | |
} | |
Synth.video.play() | |
}, | |
// access denied | |
function(){ | |
alert('you\'ll have to allow access to the webcam for this to work.'); | |
} | |
); | |
} | |
}, | |
'playAudio': function (freqs) { | |
if (!freqs.length) return; | |
var o; | |
freqs.forEach(function(freq) { | |
o = Synth.audio.ctx.createOscillator(); | |
o.frequency.value = freq; | |
o.connect(Synth.audio.ctx.destination); | |
o.noteOn(0); | |
Synth.oscs.push(o); | |
}); | |
}, | |
'removeAudio': function(oscs){ | |
Synth.oscs.splice(0, 3).forEach(function(o) { | |
o.noteOff(0); | |
}); | |
}, | |
'bindEvents': function(){ | |
Synth.comp = document.createElement('canvas'); | |
Synth.compCtx = Synth.comp.getContext('2d'); | |
this.comp.width = this.canvas.width = this.video.width = window.innerWidth; | |
this.comp.height = this.canvas.height = this.video.height = window.innerHeight; | |
// video play listener | |
document.getElementById('hide').addEventListener('click', function() { | |
this.parentNode.className = 'hidden'; | |
},false); | |
this.video.addEventListener('play', function(){ | |
var lastColor = { r: 0, g: 0, b: 0 }; | |
setInterval(function(){ | |
Synth.compCtx.drawImage(Synth.video, 0, 0, Synth.video.width, Synth.video.height); | |
var data = Synth.compCtx.getImageData(0, 0, Math.floor(Synth.video.width / 5), Math.floor(Synth.video.height / 5)); | |
var i = -4, count = 0, sample = 5, color = { r: 0, g: 0, b: 0 }, length = data.data.length; | |
// iterate over the canvas based on our sample size | |
while ( (i += sample * 4) < length) { | |
++count; | |
// extract the average color based on pixel array data | |
color.r += data.data[i]; | |
color.g += data.data[i + 1]; | |
color.b += data.data[i + 2]; | |
} | |
// floor the values | |
color.r = ~~(color.r / count); | |
color.g = ~~(color.g / count); | |
color.b = ~~(color.b / count); | |
// is there enough color shift | |
if (Math.abs(color.r - lastColor.r) > Synth.colorShiftThreshold && | |
Math.abs(color.g - lastColor.g) > Synth.colorShiftThreshold && | |
Math.abs(color.b - lastColor.b) > Synth.colorShiftThreshold) { | |
Synth.playAudio([color.r, color.g, color.b].map(function(a) { return a * Synth.multiplier })); | |
} | |
lastColor = color; | |
Synth.ctx.fillStyle = 'rgb(' + color.r + ',' + color.g + ',' + color.b + ')'; | |
Synth.ctx.fillRect(0, 0, Synth.canvas.width, Synth.canvas.height); | |
Synth.ctx.fill(); | |
}, 1000 / 25); | |
if (Synth.oscs.length > Synth.maxOscs) Synth.removeAudio(); | |
}, false); | |
}, | |
'init': function(){ | |
this.bindEvents(); | |
this.camera.init() | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<style> | |
html, body { padding: 0; margin: 0; overflow: hidden; font-family: 'Arial', 'Helvetica', sans-serif; } | |
video { display: none; width: 100%; height: 100% } | |
canvas { display: block; width: 110%; height: 110% } | |
#about { position: absolute; top: 100px; left: 100px; max-width: 25%; transition: all 250ms; } | |
a { text-decoration: none; color: black; } | |
a:hover { text-decoration: underline; } | |
.hidden { display: none | |
</style> | |
</head> | |
<body> | |
<div id="about"> | |
<h1>Average Synthesis</h1> | |
<p>This is a pure HTML5 synthesiser. It averages the color interpreted by your webcam and translates it into sound. Tones are added as colors shift. No more than nine tones may be present at any given time.</p> | |
<a href="#" id="hide">Hide this mesaage</a> | |
</div> | |
<video id="video"></video> | |
<canvas id="canvas"></canvas> | |
<script src="/application.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment