JSFiddle - React, Tailwind, and code Playground
by greweb
HTML
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css">
<div id="error" style="display:none">Sorry, AudioContext is not supported by your browser, it should work on Google Chrome.</div>
<div id="failure" style="display:none">Your browser failed to start the AudioContext, probably because you have too much web app using it. <a href="">Try again</a></div>
<div id="overlay" class="play">
Press here <i href="#" class="icon-play"></i> to start
</div>
<header>
<h1>
FM - LFO demo
<span style="float: right">
<a href="#" class="play">
play
<i class="icon-play"></i>
</a>
<a href="#" class="stop">
stop
<i class="icon-stop"></i>
</a>
</span>
</h1>
<h2></h2>
</header>
<div class="modules" style="height: 190px">
<div id="left-module" class="module">
<h3>Modulator</h3>
<div class="infos"></div>
<div class="viewport">
<canvas width="200" height="150"></canvas>
<canvas width="200" height="150"></canvas>
</div>
<div class="controls">
<p>
<label>frequency</label>
<span class="value-container">
<span class="value"></span>
Hz
</span>
<input type="range" data-bind="modulator.osc.frequency" min="0.1" max="20" step="0.1" value="2" />
</p>
</div>
</div>
<div id="right-module" class="module">
<h3>Carrier</h3>
<div class="infos"></div>
<div class="viewport">
<canvas width="200" height="150"></canvas>
<canvas width="200" height="150"></canvas>
</div>
<div class="controls">
</div>
</div>
</div>
<footer>
by <a href="http://twitter.com/greweb">@greweb</a>
</footer>
CSS
body {
min-width: 400px;
}
footer {
clear: left;
}
#failure, #error {
color: #f00;
}
#overlay {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0.8;
background: #fff;
color: #468;
text-align: center;
padding-top: 10%;
font-size: 3em;
cursor: pointer;
z-index: 1000;
font-weight: bold;
}
#overlay:hover {
opacity: 0.9;
}
a {
color: #49c;
text-decoration: none;
-webkit-transition: 200ms;
}
a:hover {
text-decoration: none;
color: #169;
}
h1 {
color: #9bd;
font-family: Helvetica, sans-serif;
}
h3 {
font-size: 1.0em;
margin: 0.2em;
color: #369;
}
canvas {
width: 100%;
background: #fff;
}
.module {
float: left;
width: 47%;
margin: 0.5%;
padding: 1%;
background: #f3f7fc;
border: 1px solid #9cf;
border-radius: 3px;
box-sizing: border-box;
-webkit-box-sizing: border-box;
height: 100%;
}
#left-module {
margin-right: 1%;
position: relative;
}
#right-module {
margin-left: 1%;
}
#left-module:after {
position: absolute;
bottom: -10px;
right: -5%;
width: 5%;
border-top: 2px solid #369;
color: #369;
content: "freq";
font-size: 0.8em;
font-family: monospace;
}
.module .viewport {
position: relative;
box-sizing: border-box;
-webkit-box-sizing: border-box;
overflow: auto;
margin-bottom: 5px;
clear: both;
}
.module canvas {
width: 49%;
}
.module canvas:first-child {
float: left;
}
.module canvas:last-child {
float: right;
}
.module h3 {
float: left;
}
.module .controls {
clear: both;
}
.module .controls p input {
width: 100%;
}
.module .controls label {
color: #c06;
}
.module .controls .value-container {
float: right;
color: #c06;
}
.module .controls .value {
font-weight: bold;
}
.module .infos {
color: #666;
font-family: monospace;
font-size: 0.8em;
white-space: pre;
float: right;
}
JavaScript
REFRESH_RATE = 30;
VOLUME = 0.5;
var AudioContext = window.webkitAudioContext; // only webkitAudioContext supports Oscillator for now
if (!AudioContext) {
$('#error').show();
return;
}
var audioCtx;
var destination;
var carrier, modulator, lspectrum, rspectrum, lwaveform, rwaveform;
function demo () {
try {
audioCtx = new AudioContext();
}
catch (e) {
$('#failure').show();
return;
}
destination = audioCtx.createGain();
destination.gain.value = VOLUME;
destination.connect(audioCtx.destination);
// Config of the demo
carrier = new Carrier("sine", 400);
modulator = new Modulator("sine", 500, 300);
lspectrum = new SpectrumAnalyzer(modulator.osc, 0, 100);
rspectrum = new SpectrumAnalyzer(carrier.gain, 0, 1000);
lwaveform = new WaveformAnalyzer(modulator.osc, 2048);
rwaveform = new WaveformAnalyzer(carrier.gain);
modulator.gain.connect(carrier.osc.frequency);
carrier.gain.gain.value = 0;
carrier.gain.connect(destination);
}
var renderInterval;
// A modulator have a oscillator and a gain
function Modulator (type, freq, gain) {
this.osc = audioCtx.createOscillator();
this.gain = audioCtx.createGainNode();
this.osc.type = type;
this.osc.frequency.value = freq;
this.gain.gain.value = gain;
this.osc.connect(this.gain);
this.osc.start(0);
}
Modulator.prototype.toString = function () {
return "freq="+this.osc.frequency.value.toFixed(1)+
" amp="+this.gain.gain.value.toFixed(0);
}
function Carrier (type, freq) {
this.osc = audioCtx.createOscillator();
this.gain = audioCtx.createGainNode();
this.osc.type = type;
this.osc.frequency.value = freq;
this.osc.connect(this.gain);
this.osc.start(0);
}
Carrier.prototype.toString = function () {
return "freq="+this.osc.frequency.value.toFixed(1)+
" amp="+this.gain.gain.value.toFixed(1);
}
function SpectrumAnalyzer (audioNode, minRange, maxRange) {
this.audioNode =...