Theramine

theramine built with web audio API oscillator and filter

by Soviut

HTML

<h1>Click to Play Note</h1>
<p>Horizontal position is frequency, vertical is filter.</p>

CSS

html {
    font-family: arial, helvetica, san-serif;
    
    transition: background-color 100ms ease-in-out;
}

html:active {
    background-color: cornflowerblue;
    cursor: move;
}

JavaScript

var context = new AudioContext();
console.log(context);

var amp = context.createGain();
amp.connect(context.destination);
amp.gain.value = 0.5;

var osc;
var maxFreq = 3000;
var minFreq = 100;
var waveforms = ['sine', 'sawtooth', 'square', 'triangle'];

var filter;
var maxFilterFreq = 3000;
var minFilterFreq = 100;

var playing = false;

function updateAudio(e) {
    filter.frequency.value = (1 - e.pageY / $(document).height()) * (maxFilterFreq - minFilterFreq);
    osc.frequency.value = (e.pageX / $(document).width()) * (maxFreq - minFreq);
    //amp.gain.value = 1 - (e.pageY / $(document).height()); 
}

$doc = $(document);

$doc.on('mousedown touchstart', function(e) {
    e.preventDefault();
    playing = true;
    filter = context.createBiquadFilter();
    filter.type = 'lowpass';
    filter.connect(amp);

    if (osc) {
        osc.stop(0);
    }

    osc = context.createOscillator();
    osc.connect(filter);
    osc.type = waveforms[Math.floor(Math.random() * waveforms.length)];

    osc.start(0);
    updateAudio(e);
});

$doc.on('mousemove touchmove', function(e) {
    e.preventDefault();
    if (playing) {
        updateAudio(e);
    }
});

$doc.on('mouseup touchend', function(e) {
    e.preventDefault();
    playing = false;
    osc.stop(0);
});