<head>
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,600' rel='stylesheet' type='text/css'>
<title>Frequency Response | Web Audio API</title>
</head>
<body>
<div id="container">
<div id="wrapper">
<div id="content">
<div class="post">
<h2>Frequency Response</h2>
<p>
<p>A sample showing the frequency response graphs of various kinds of <code>BiquadFilterNodes</code>.</p>
</p>
<p>
<!-- Slider stuff -->
<script type="text/javascript" src="events.js"></script>
<p><style type="text/css"></style>
<style type="text/css">
#slider { margin: 10px; }
</style></p>
<script src="/static/js/shared.js"></script>
<script type="text/javascript" src="frequency-response-sample.js"></script>
<div id="info">
</div>
<p><canvas id="canvasID" width="500" height="250" style="float: left;">
</canvas></p>
<p><br><br></p>
<!-- Sliders and other controls will be added here -->
<div style="display: inline-block; margin-left: 10px;" id="controls">
<select onchange="changeFilterType(this.value);">
<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>
<div> <input id="frequencySlider" type="range" min="0" max="1" step="0.01" value="0" style="height: 20px; width: 200px;"> <span id="frequency-value" style="position:relative; top:-5px;">frequency = 1277.46 Hz</span> </div> <br> <div> <input id="QSlider" type="range" min="0" max="20" step="0.01" value="0" style="height: 20px; width: 200px;"> <span id="Q-value" style="position:relative; top:-5px;">Q = 8.59 dB</span> </div> <br> <div> <input id="gainSlider" type="range" min="0" max="5" step="0.01" value="0" style="height: 20px; width: 200px;"> <span id="gain-value" style="position:relative; top:-5px;">gain = 3.12</span> </div> <br> </div>
</p>
</div>
</body>
</html>
body {
background: black;
font-family: 'Open Sans', sans-serif;
color: #ccc;
}
#container {
margin: 0 auto;
width: 640px;
background: #111;
padding: 20px;
border: 1px #222 solid;
}
#header h1 { margin: 0; }
#nav ul { margin: 0; }
#nav li {
font-weight: bold;
float: right;
list-style: none;
margin-left: 10px;
}
#header a, #nav a { color: rgb(28, 161, 202); text-decoration: none; }
#header a:hover, #nav a:hover { color: rgb(157, 204, 255); text-decoration: underline; }
article a { color: #FEFEF2; text-decoration: none; }
article a:hover { color: white; text-decoration: underline; }
a { color: #FEF0C9; text-decoration: none; }
a:hover { color: #FEF6E4; text-decoration: underline; }
article {
font-weight: bold;
margin: 20px;
padding: 20px;
text-align: center;
cursor: pointer;
color: black;
}
#content article:nth-child(6n+1) { background: #FA6B67; }
#content article:nth-child(6n+2) { background: #70CA5D; }
#content article:nth-child(6n+3) { background: #3CC9E4; }
#content article:nth-child(6n+4) { background: #EC945E; }
#content article:nth-child(6n+5) { background: #7189DE; }
#content article:nth-child(6n+6) { background: #E798DC; }
.sample .name {
font-size: 22px;
padding-bottom: 5px;
color: #fee;
}
.sample .info {
color: #B0F0F8;
}
input[type='range'] {
appearance: none;
background-color: gray;
height: 20px;
border-radius: 15px;
padding: 0 3px;
}
input[type='range']::slider-thumb {
vertical-align: middle;
appearance: none;
background-color: #444;
width: 16px;
height: 16px;
border-radius: 16px;
}
input[type="checkbox"] {
display:none;
}
input[type="checkbox"] + label span {
display:inline-block;
width:19px;
height:19px;
margin:-1px 4px 0 0;
vertical-align:middle;
background:url(/static/images/check_radio_sheet.png) left top no-repeat;
cursor:pointer;
}
input[type="checkbox"]:checked + label span {
background:url(/static/images/check_radio_sheet.png) -19px top no-repeat;
}
// ------ shared.js
// Start off by initializing a new context.
context = new (window.AudioContext || window.webkitAudioContext)();
var frequencyRenderer;
//---------- events.js
function configureSlider(name, value, min, max, handler) {
// var controls = document.getElementById("controls");
//
var divName = name + "Slider";
var slider = document.getElementById(divName);
slider.min = min;
slider.max = max;
slider.value = value;
slider.oninput = function() { handler(0, this); };
}
// Events
// init() once the page has finished loading.
window.onload = init;
var filter;
var filters = [];
var frequency = 2000;
var resonance = 5;
var gain = 2;
var canvas;
var canvasContext;
function frequencyHandler(event, ui) {
var value = ui.value;
var nyquist = context.sampleRate * 0.5;
var noctaves = Math.log(nyquist / 10.0) / Math.LN2;
var v2 = Math.pow(2.0, noctaves * (value - 1.0));
var cutoff = v2*nyquist;
var info = document.getElementById("frequency-value");
info.innerHTML = "frequency = " + (Math.floor(cutoff*100)/100) + " Hz";
filter.frequency.value = cutoff;
frequencyRenderer.draw(filters);
}
function resonanceHandler(event, ui) {
var value = ui.value;
var info = document.getElementById("Q-value");
info.innerHTML = "Q = " + (Math.floor(value*100)/100) + " dB";
filter.Q.value = value; // !! Q value not same as resonance...
frequencyRenderer.draw(filters);
}
function gainHandler(event, ui) {
var value = ui.value;
var info = document.getElementById("gain-value");
info.innerHTML = "gain = " + (Math.floor(value*100)/100);
filter.gain.value = value;
frequencyRenderer.draw(filters);
}
function initAudio() {
filter = context.createBiquadFilter();
filter.Q.value = 5;
filter.frequency.value = 2000;
filter.gain.value = 2;
filter.connect(context.destination);
filters.push(filter);
// * filters from multiband EQ
/*
[60, 170, 350, 1000, 3500, 10000].forEach(function(freq, i) {
var eq = context.createBiquadFilter();
eq.frequency.value = freq;
eq.type = "peaking";
eq.gain.value = -30+Math.random()*60;
filters.push(eq);
});
*/
}
function init() {
initAudio();
canvas = document.getElementById('canvasID');
canvasContext = canvas.getContext('2d');
configureSlider("frequency", 0.68, 0, 1, frequencyHandler);
configureSlider("Q", resonance, 0, 30, resonanceHandler);
configureSlider("gain", gain, -30, 30, gainHandler);
frequencyRenderer = new FilterFrequencyResponseRenderer(canvas, context);
frequencyRenderer.draw(filters);
}
function changeFilterType( value ) {
filter.type = value;
frequencyRenderer.draw(filters);
}
// ----------- FILTER RESPONSE RENDERER
function FilterFrequencyResponseRenderer(canvas, audioCxt) {
var c = canvas;
var ctx = c.getContext('2d');
var width = c.width;
var height = c.height;
var audioContext = audioCxt;
var curveColor = "rgb(224,27,106)";
var playheadColor = "rgb(80, 100, 80)";
var gridColor = "rgb(100,100,100)";
var textColor = "rgb(81,127,207)";
var dbScale = 60;
var pixelsPerDb;
var dbToY = function(db) {
var y = (0.5 * height) - pixelsPerDb * db;
return y;
};
var draw = function(filters) {
ctx.save();
ctx.clearRect(0, 0, width, height);
var frequencyHz = new Float32Array(width);
var magResponse = new Float32Array(width);
var phaseResponse = new Float32Array(width);
var nyquist = 0.5 * audioContext.sampleRate;
var noctaves = 11;
filters.forEach(function(filt) {
ctx.strokeStyle = curveColor;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0, 0);
pixelsPerDb = (0.5 * height) / dbScale;
// First get response.
for (var i = 0; i < width; ++i) {
var f = i / width;
// Convert to log frequency scale (octaves).
f = nyquist * Math.pow(2.0, noctaves * (f - 1.0));
frequencyHz[i] = f;
}
filt.getFrequencyResponse(frequencyHz, magResponse, phaseResponse);
/*
var magResponseOfFilter = new Float32Array(width);
filt.getFrequencyResponse(frequencyHz, magResponseOfFilter, phaseResponse);
// sum it to magResponse
for(var l = 0; l < width; l++) {
magResponse[l] = magResponseOfFilter[l];
}
*/
for (var i = 0; i < width; ++i) {
var f = magResponse[i];
var response = magResponse[i];
var dbResponse = 20.0 * Math.log(response) / Math.LN10;
var x = i;
var y = dbToY(dbResponse);
if ( i === 0 )
ctx.moveTo(x,y);
else
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = gridColor;
// Draw frequency scale.
for (var octave = 0; octave <= noctaves; octave++) {
var x = octave * width / noctaves;
ctx.strokeStyle = gridColor;
ctx.moveTo(x, 30);
ctx.lineTo(x, height);
ctx.stroke();
var f = nyquist * Math.pow(2.0, octave - noctaves);
var value = f.toFixed(0);
var unit = 'Hz';
if (f > 1000) {
unit = 'KHz';
value = (f/1000).toFixed(1);
}
ctx.textAlign = "center";
ctx.strokeStyle = textColor;
ctx.strokeText(value + unit, x, 20);
}
// Draw 0dB line.
ctx.beginPath();
ctx.moveTo(0, 0.5 * height);
ctx.lineTo(width, 0.5 * height);
ctx.stroke();
// Draw decibel scale.
for (var db = -dbScale; db < dbScale - 10; db += 10) {
var y = dbToY(db);
ctx.strokeStyle = textColor;
ctx.strokeText(db.toFixed(0) + "dB", width - 40, y);
ctx.strokeStyle = gridColor;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
});
ctx.restore();
};
return {
draw: draw
};
}
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |