Skip to content

Instantly share code, notes, and snippets.

@jgv
Created October 10, 2013 17:21
Show Gist options
  • Save jgv/6922177 to your computer and use it in GitHub Desktop.
Save jgv/6922177 to your computer and use it in GitHub Desktop.
webcam synth
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()
}
};
<!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