<div id="synth" tabindex="1">
Click to start/stop the theremin.
</div>
<div id="controls">
<ul>
<li>
<label>
Waveform:
<select id="oscillator-type">
<option value="sine">sine</option>
<option value="square">square</option>
<option value="sawtooth">sawtooth</option>
<option value="triangle">triangle</option>
</select>
</label>
</li>
<li>
<label>
LFO freq:
<input id="lfo-freq" type="range" min="0" max="10" step="0.25" value="0"/>
</label>
</li>
<li>
<label>
Gain:
<input id="gain" type="range" />
</label>
</li>
<li>
<label>
Filter:
<select id="filter">
<option value="none">none</option>
<option value="lowpass">lowpass</option>
<option value="highpass">highpass</option>
<option value="bandpass">bandpass</option>
<option value="lowshelf">lowshelf</option>
<option value="highshelf">highshelf</option>
<option value="peaking">peaking</option>
<option value="notch">notch</option>
<option value="allpass">allpass</option>
</select>
</label>
</li>
<li>
<label>
Distortion:
<input id="distortion" type="range" min="0" max="100" step="1" value="0"/>
</label>
</li>
</ul>
</div>
@mixin uisection ($background) {
position: absolute;
width: 300px;
height: 300px;
background-color: $background;
color: white;
}
#synth {
@include uisection(#222222);
}
#controls {
@include uisection(#444444);
left: 320px;
}
select, input {
margin-left: 0.5em;
}
li {
list-style-type: none;
margin-bottom: 1em;
}
View Compiled
// Create an audio context
var audio_context = window.AudioContext || window.webkitAudioContext;
var context = new audio_context();
// Create Audio Nodes
var oscillator = context.createOscillator();
var lfo = context.createOscillator();
var lfo_amp = context.createGain();
var filter = context.createBiquadFilter();
var distortion = context.createWaveShaper();
distortion.curve = makeDistortionCurve(0);
distortion.oversample = '4x';
var volume = context.createGain();
// Create Audio Routing Graph
lfo.connect(lfo_amp);
lfo_amp.connect(oscillator.frequency);
oscillator.connect(distortion);
distortion.connect(volume);
volume.connect(context.destination);
// Get references to UI elements
var synth = document.getElementById("synth");
var oscillatorTypeSelector = document.getElementById("oscillator-type");
var LFOFrequencyControl = document.getElementById("lfo-freq");
var gainControl = document.getElementById("gain");
var filterControl = document.getElementById("filter");
var distortionControl = document.getElementById("distortion");
// Initialise State
var isOn = false;
gainControl.value = 0;
changeLFOGain();
// Set Theremin Event Listeners
synth.addEventListener("click", function(){
if (isOn) {
oscillator.stop();
lfo.stop();
isOn = false;
}
else {
oscillator.start();
lfo.start();
isOn = true;
}
});
synth.addEventListener("mousemove", changeVolume);
synth.addEventListener("mousemove", changeOscillatorPitch);
// Set Controls Event Listeners
oscillatorTypeSelector.addEventListener("change", changeWaveform);
LFOFrequencyControl.addEventListener("change", changeLFOFrequency);
gainControl.addEventListener("change", changeLFOGain);
filterControl.addEventListener("change", changeFilter);
distortionControl.addEventListener("change", changeDistortion);
// Theremin Event Handlers
function changeOscillatorPitch(event) {
oscillator.frequency.value = 350 + 2 * event.clientX;
}
function changeVolume(event) {
volume.gain.value = event.offsetY / synth.clientHeight;
}
// Controls Event Handlers
function changeWaveform() {
oscillator.type = oscillatorTypeSelector.value;
}
function changeLFOFrequency() {
lfo.frequency.value = LFOFrequencyControl.value;
}
function changeLFOGain() {
lfo_amp.gain.value = gainControl.value;
}
function changeFilter() {
console.log("changeFilter")
if (filterControl.value === "none") {
filter.disconnect(distortion);
oscillator.disconnect(filter);
oscillator.connect(distortion);
}
else {
console.log("disconnecting")
filter.type = filterControl.value;
oscillator.disconnect(distortion);
oscillator.connect(filter);
filter.connect(distortion);
}
}
function changeDistortion() {
distortion.curve = makeDistortionCurve(parseInt(distortionControl.value));
}
function makeDistortionCurve(amount) {
var k = typeof amount === 'number' ? amount : 0,
n_samples = 44100,
curve = new Float32Array(n_samples),
deg = Math.PI / 180,
i = 0,
x;
for ( ; i < n_samples; ++i ) {
x = i * 2 / n_samples - 1;
curve[i] = ( 3 + k ) * x * 20 * deg / ( Math.PI + k * Math.abs(x) );
}
return curve;
};
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.