Skip to content

Instantly share code, notes, and snippets.

@velipso
Created March 5, 2020 02:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save velipso/3eb9f963bdf4c80733cf3b81d22c68f6 to your computer and use it in GitHub Desktop.
Save velipso/3eb9f963bdf4c80733cf3b81d22c68f6 to your computer and use it in GitHub Desktop.
Random noise color
<!doctype html>
<html lang="en">
<head><title>Random Noise Color</title></head>
<body style="background-color: #eef;">
<div id="ranges"></div>
<canvas width="2048" height="1200" style="width: 1024px; height: 600px;" id="cnv"></canvas>
<script>
var cnv = document.getElementById('cnv');
var ctx = cnv.getContext('2d');
var mix = {};
var setVol = function(){};
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, cnv.width, cnv.height);
ctx.fillStyle = '#000';
ctx.font = '80px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('Click to play', cnv.width / 2, cnv.height / 2);
cnv.addEventListener('click', run);
(function(){
var ranges = document.getElementById('ranges');
([
'volume',
'red',
'pink',
'white',
'blue',
'purple'
]).forEach(function(key){
var p = document.createElement('p');
var span = document.createElement('span');
span.style.display = 'inline-block';
span.style.width = '100px';
var input = document.createElement('input');
input.type = 'range';
input.min = 0;
input.max = 120;
input.style.width = '900px';
input.value = key === 'white' ? 100 : 0;
function update(){
mix[key] = parseFloat(input.value) / 100;
span.innerHTML = key + ' ' + input.value + '%';
if (key === 'volume')
setVol();
}
input.addEventListener('change', update);
input.addEventListener('input', update);
update();
p.appendChild(span);
p.appendChild(input);
if (key === 'pink'){
var select = document.createElement('select');
for (var i = 1; i <= 16; i++){
var option = document.createElement('option');
option.value = '' + i;
option.appendChild(document.createTextNode(option.value));
select.appendChild(option);
}
select.selectedIndex = 5;
select.addEventListener('change', function(){
pinkNoise = makePinkNoise(parseFloat(select.options[select.selectedIndex].value));
});
p.appendChild(select);
}
ranges.appendChild(p);
});
})();
var tot = [];
for (var i = 0; i < 2048; i++)
tot.push(0);
var totmax = 50;
var totwin = [];
function randfloat(){
return Math.random() * 2 - 1;
}
function clip(v){
if (v < -1)
return -1;
if (v > 1)
return 1;
return v;
}
var whiteNoise = function(){
return clip(randfloat());
};
function makePinkNoise(depth){
var o = 0;
var rnd = [];
var S = 0;
for (var i = 0; i < (depth - 1); i++){
var r = randfloat();
S += r;
rnd.push(r);
}
return function(){
var idx = 0;
var bitmask = 2;
for (var idx = 0; idx < (depth - 1); idx++, bitmask *= 2){
if ((o & bitmask) != 0){
S -= rnd[idx];
rnd[idx] = randfloat();
S += rnd[idx];
}
}
o = (o + 1) % (1 << depth);
return clip((randfloat() + S) / depth);
};
}
var pinkNoise = makePinkNoise(6);
var redNoise = (function(){
var S = 0;
return function(){
var r, o;
while (true){
r = randfloat();
o = S + r;
if (o >= -8 && o <= 8)
break;
}
S = o;
return clip(o / 8);
};
})();
function makeBlueNoise(depth){
var o = 0;
var rnd = [];
var S = 0;
for (var i = 0; i < (depth - 1); i++){
var r = randfloat();
S += r;
rnd.push(r);
}
return function(){
var idx = 0;
var bitmask = 2;
for (var idx = 0; idx < (depth - 1); idx++, bitmask *= 2){
if ((o & bitmask) != 0){
S -= rnd[idx];
rnd[idx] = randfloat();
S += rnd[idx];
}
}
o = (o + 1) % (1 << depth);
return clip((randfloat() + S) / depth);
};
}
var blueNoise = makeBlueNoise(6);
var purpleNoise = (function(){
var S = 0;
return function(){
var r, o;
while (true){
r = randfloat();
o = S - r;
if (o >= -8 && o <= 8)
break;
}
S = r;
return clip(o / 8);
};
})();
function nextSample(){
return redNoise() * mix.red +
pinkNoise() * mix.pink +
whiteNoise() * mix.white +
blueNoise() * mix.blue +
purpleNoise() * mix.purple;
}
function run(){
cnv.removeEventListener('click', run);
var rate = 48000;
var actx = new AudioContext({ sampleRate: rate });
var dest = actx.destination;
var sn = actx.createScriptProcessor(1024, 0, 1);
sn.onaudioprocess = function(e){
var outputBuffer = e.outputBuffer;
var outputData = outputBuffer.getChannelData(0);
for (var i = 0; i < outputData.length; i++)
outputData[i] = clip(nextSample());
};
var gain = actx.createGain();
setVol = function(){
gain.gain.setValueAtTime(mix.volume, actx.currentTime);
};
setVol();
gain.connect(dest);
sn.connect(gain);
var az = actx.createAnalyser();
az.fftSize = 4096;
az.smoothingTimeConstant = 0;
sn.connect(az);
function draw(){
var ar = new Uint8Array(az.fftSize);
az.getByteFrequencyData(ar);
totwin.push(ar);
if (totwin.length > totmax){
var oldar = totwin.shift();
for (var i = 0; i < 2048; i++)
tot[i] -= oldar[i];
}
for (var i = 0; i < 2048; i++)
tot[i] += ar[i];
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, cnv.width, cnv.height);
for (var sx = 0; sx < 2048; sx++){
var y = tot[sx] / totwin.length;
ctx.beginPath();
ctx.moveTo(sx, cnv.height);
ctx.lineTo(sx, cnv.height - y * cnv.height / 255);
ctx.stroke();
}
}
setInterval(draw, 0);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment