<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Sine wave</title>
</head>
<body>
<div class="flex">
<span id="range-value"></span>
<input type="range" id="freq" min=440 max=12000 value=440>
</div>
<div class="flex">
<span id="volume-value"></span>
<input type="range" id="volume" min=0 max=100 value=0>
</div>
<button>Toggle sound</button>
<main><canvas></canvas></main>
</body>
</html>
* {
box-sizing: border-box;
background: white;
font-family: sans-serif;
font-size: 14px;
}
input[type=range] {
width: 100%;
}
.flex {
display: flex;
}
div {
position: relative;
}
#range-value,
#volume-value {
width: 80px;
text-align: right;
}
#range-value:after {
content: 'hz'
}
#volume-value:after {
content: '%'
}
#volume-value:before {
content: 'vol '
}
button {
margin: 10px 0;
cursor: pointer;
border: 0;
background: rgb(3, 169, 244);
color: white;
padding: 10px 20px;
border-radius: 5px;
}
button:hover {
background: #1976d2;
}
canvas {
border: 1px solid rgb(3, 169, 244);
}
//noprotect
// apologies for the horrible code, I was being lazy
// globalsFTW ¯\_(ツ)_/¯
let gainNode;
let context;
let audio;
let analyser;
let bufferLength;
let dataArray;
const updatePosition = (input, output) => () => {
output.innerHTML = input.value;
output.style.left = `calc(${(input.value - input.min) / (input.max - input.min) * 95}%)`;
}
function getAudio() {
// create web audio api context
context = new (window.AudioContext || window.webkitAudioContext)();
// create Oscillator node
const oscillator = context.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.value = range.value; // value in hertz
// oscillator.connect(audioCtx.destination);
gainNode = context.createGain();
// Connect the source to the gain node.
oscillator.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);
const fraction = parseInt(volume.value) / parseInt(volume.max);
gainNode.gain.value = fraction * fraction;
analyser = context.createAnalyser();
gainNode.connect(analyser);
bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
analyser.fftSize = 1024;
oscillator.start();
return oscillator;
}
function draw() {
requestAnimationFrame(draw);
if (!analyser) {
return;
}
ctx.clearRect(0, 0, WIDTH, HEIGHT);
// analyser.getByteTimeDomainData(dataArray);
analyser.getByteFrequencyData(dataArray);
var barWidth = ((WIDTH) / bufferLength) * 1.5;
var barHeight;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
barHeight = dataArray[i];
ctx.fillStyle = 'rgb(3,169,244)';
ctx.fillRect(x+.5,HEIGHT-barHeight/2+.5,barWidth,barHeight);
x += barWidth + 1;
}
}
const $$ = q => Array.from(document.querySelectorAll(q));
const $ = document.querySelector.bind(document);
//-----
const position = $('#range-value');
const range = $('#freq');
const updateFreq = updatePosition(freq, position);
range.oninput = () => {
updateFreq();
if (audio) audio.frequency.value = range.value; // value in hertz
}
updateFreq();
const vposition = $('#volume-value');
const volume = $('#volume');
const updateVolume = updatePosition(volume, vposition);
volume.oninput = () => {
updateVolume();
const fraction = parseInt(volume.value) / parseInt(volume.max);
gainNode.gain.value = fraction * fraction;
}
updateVolume();
// button
const button = $('button');
let running = false;
button.onclick = function () {
if (running) {
audio.stop();
} else {
audio = getAudio();
}
running = !running;
}
// canvas
const canvas = $('canvas');
const ctx = canvas.getContext('2d');
const WIDTH = canvas.width = canvas.parentNode.offsetWidth;
const HEIGHT = canvas.height = canvas.parentNode.offsetHeight;
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. |