html5, canvas, and some funky music - kidding! do the math on the object :)
Created
July 22, 2019 07:30
-
-
Save beseidel/bbbe67a66d37dd95a187890c3aa5bcb4 to your computer and use it in GitHub Desktop.
musical mind game
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="container"> | |
<div id="main"> | |
<audio id="theSong" controls /> | |
<div id="controls"> | |
<p> | |
<select id="filtersDropdown"> | |
</select> | |
</p> | |
<canvas id="canvas" width="50" height ="20"></canvas> | |
<p> | |
<input id="frequencySlider" title="Frequency" type="range" min="0" max="2000" value="1000" /> | |
<input id="qSlider" title="Q" type="range" min="1" max="100" value="10" /> | |
<input id="gainSlider" title="Gain" disabled type="range" min="1" max="100" value="20" /> | |
</p> | |
</div> | |
</div> | |
<div class="circles"> | |
<canvas id="concentric_circles" width="600" height="600"></canvas> | |
</div> | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var AudioContext = window.AudioContext || | |
window.webkitAudioContext; | |
var audioContext; | |
var biquadFilter; | |
var frequencySlider = document.getElementById("frequencySlider"); | |
var qSlider = document.getElementById("qSlider"); | |
var gainSlider = document.getElementById("gainSlider"); | |
var audio; | |
// All Web Audio API filters | |
// https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode/type | |
// q and gain controls if the corresponding slider | |
// should be enabled | |
var filters = { "lowpass": { | |
q: true, | |
gain: false | |
}, "highpass": { | |
q: true, | |
gain: false | |
}, "bandpass": { | |
q: true, | |
gain: false | |
}, "lowshelf": { | |
q: false, | |
gain: true | |
}, "highshelf": { | |
q: false, | |
gain: true | |
}, "peaking": { | |
q: true, | |
gain: true | |
}, "notch": { | |
q: true, | |
gain: false | |
}, "allpass": { | |
q: true, | |
gain: false | |
} | |
}; | |
var canvas = document.getElementById("canvas"); | |
var canvasContext = canvas.getContext("2d"); | |
var frequencyBars = 100; | |
// Array containing all the frequencies we want to get | |
// response for when calling getFrequencyResponse() | |
var myFrequencyArray = new Float32Array(frequencyBars); | |
for(var i = 0; i < frequencyBars; ++i) { | |
myFrequencyArray[i] = 2000/frequencyBars*(i+1); | |
} | |
// We receive the result in these two when calling | |
// getFrequencyResponse() | |
var magResponseOutput = new Float32Array(frequencyBars); // magnitude | |
var phaseResponseOutput = new Float32Array(frequencyBars); | |
audioContext = new AudioContext(); | |
window.addEventListener("load", function(e) { | |
audio = document.getElementById("theSong"); | |
audio.crossOrigin = "anonymous"; | |
var source = audioContext.createMediaElementSource(audio); | |
biquadFilter = audioContext.createBiquadFilter(); | |
biquadFilter.type = "lowpass"; | |
biquadFilter.frequency.value = 1000; | |
biquadFilter.Q.value = 10; | |
biquadFilter.gain.value = 20; | |
source.connect(biquadFilter); | |
biquadFilter.connect(audioContext.destination); | |
updateFrequencyResponse(); | |
}, false); | |
function drawFrequencyResponse(mag, phase) { | |
canvasContext.clearRect(0, 0, canvas.width, canvas.height); | |
var barWidth = 400 / frequencyBars; | |
// Magnitude | |
canvasContext.strokeStyle = "white"; | |
canvasContext.beginPath(); | |
for(var frequencyStep = 0; frequencyStep < frequencyBars; ++frequencyStep) { | |
canvasContext.lineTo( | |
frequencyStep * barWidth, | |
canvas.height - mag[frequencyStep]*90); | |
} | |
canvasContext.stroke(); | |
// Phase | |
canvasContext.strokeStyle = "red"; | |
canvasContext.beginPath(); | |
for(var frequencyStep = 0; frequencyStep < frequencyBars; ++frequencyStep) { | |
canvasContext.lineTo( | |
frequencyStep * barWidth, | |
canvas.height - (phase[frequencyStep]*90 + 300)/Math.PI); | |
} | |
canvasContext.stroke(); | |
} | |
function updateFrequencyResponse() { | |
biquadFilter.getFrequencyResponse( | |
myFrequencyArray, | |
magResponseOutput, | |
phaseResponseOutput); | |
drawFrequencyResponse(magResponseOutput, phaseResponseOutput); | |
} | |
frequencySlider.addEventListener("change", function () { | |
biquadFilter.frequency.value = this.value; | |
}); | |
frequencySlider.addEventListener("mousemove", function () { | |
biquadFilter.frequency.value = this.value; | |
updateFrequencyResponse(); | |
}); | |
qSlider.addEventListener("mousemove", function () { | |
biquadFilter.Q.value = this.value; | |
updateFrequencyResponse(); | |
}); | |
gainSlider.addEventListener("mousemove", function () { | |
biquadFilter.gain.value = this.value; | |
updateFrequencyResponse(); | |
}); | |
var filtersDropdown = document.getElementById("filtersDropdown"); | |
for(var item in filters) { | |
var option = document.createElement("option"); | |
option.innerHTML = item; | |
// This will cause a re-flow of the page but we don't care | |
filtersDropdown.appendChild(option); | |
}; | |
function filterClicked (event) { | |
event = event || window.event; | |
var target = event.target || event.srcElement; | |
var filterName = target.value; | |
biquadFilter.type = filterName; | |
updateFrequencyResponse(); | |
qSlider.disabled = !filters[filterName].q; | |
gainSlider.disabled = !filters[filterName].gain; | |
}; | |
filtersDropdown.addEventListener("change", filterClicked, false); | |
//////////////////////////// | |
// Soundcloud stuff below // | |
//////////////////////////// | |
function get(url, callback) { | |
var request = new XMLHttpRequest(); | |
request.onreadystatechange = function() { | |
if (request.readyState === 4 && request.status === 200) { | |
callback(request.responseText); | |
} | |
} | |
request.open("GET", url, true); | |
request.send(null); | |
} | |
var clientParameter = "client_id=aae0cd3ce269784234bb78aa6d485394" | |
var trackPermalinkUrl = "https://soundcloud.com/spinninrecords/project-t-martin-garrix-remix"; | |
//var trackPermalinkUrl = | |
"https://soundcloud.com/aviciiofficial/avicii-levels-original-mix"; | |
function findTrack() { | |
get("https://api.soundcloud.com/resolve.json?url=" + trackPermalinkUrl + "&" + clientParameter, | |
function (response) { | |
var trackInfo = JSON.parse(response); | |
audio.src = trackInfo.stream_url + "?" + clientParameter; | |
} | |
); | |
}; | |
findTrack(); | |
})(); | |
var canvas = document.getElementById("concentric_circles"); | |
var ctx = canvas.getContext("2d"); | |
var canvasWidth = canvas.width; | |
var canvasHeight = canvas.height; | |
var circles = new Array(); | |
function Circle(angle, radius, rRadius, iX, iY) { | |
this.angle = angle; | |
this.radius = radius; | |
this.rRadius = rRadius; | |
this.iX = iX; | |
this.iY = iY; | |
this.inc = 1; | |
} | |
Circle.prototype.add = function () { | |
this.angle += this.inc; | |
this.cX = this.iX + this.rRadius * Math.cos(this.angle); | |
this.cY = this.iY + this.rRadius * Math.sin(this.angle); | |
if (this.angle >= (Math.PI * 2)) { | |
this.angle = 0; | |
this.inc = .01 + Math.random() * .1; | |
} | |
var rColor = 'rgba(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ', .3)'; | |
ctx.beginPath(); | |
ctx.arc(this.cX, this.cY, this.radius, 0, Math.PI * 2); | |
ctx.closePath(); | |
ctx.fillStyle = rColor; | |
ctx.fill(); | |
ctx.strokeStyle = rColor; | |
ctx.stroke(); | |
}; | |
function drawCC() { | |
for (var i = 0; i < 30 ; i++) { | |
for (var j = 0; j < 50+Math.random() * 200 ; j++) { | |
var iX = canvasWidth / 2; | |
var iY = canvasHeight / 3; | |
var radius = i; | |
var rRadius = 30*i; | |
var angle =100+i; | |
var circle = new Circle(angle, radius, rRadius, iX, iY); | |
circles.push(circle); | |
} | |
} | |
requestAnimationFrame(draw); | |
} | |
drawCC(); | |
function draw() { | |
ctx.clearRect(0, 0, canvasWidth, canvasHeight); | |
ctx.fillStyle = "#000000"; | |
ctx.fillRect(0, 0, canvasWidth, canvasHeight); | |
for (var i = 0; i < circles.length; i++) { | |
var circle = circles[i]; | |
circle.add(); | |
} | |
requestAnimationFrame(draw); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background-color: black; | |
text-align: center; | |
margin: 0 auto; | |
position: relative; | |
} | |
.float { | |
position: absolute; | |
left: 0; | |
top: 0; | |
} | |
.circles{ | |
width: 85%; | |
height: auto; | |
border: 4px solid black; | |
margin-left: 75px; | |
} | |
#main{ | |
width: 100%; | |
height: 10%; | |
} | |
.top-container { | |
color: #fff; | |
font-family: sans-serif; | |
width: 100%; | |
padding-top: 40px; | |
padding-bottom: 40px; | |
margin: 0 auto; | |
text-align: center; | |
margin-bottom: 40px; | |
border-bottom: 2px solid rgba(0, 0, 0, 0); | |
background-color: #fff; | |
} | |
.top-container h1 { | |
font-size: 28px; | |
} | |
.top-container h2 { | |
font-size: 14px; | |
font-weight: 100; | |
opacity: 0.6; | |
} | |
#canvas { | |
border: 4px solid black; | |
background-color: rgba(128, 128, 128, 0.16); | |
} | |
input:disabled { | |
opacity: 0.1; | |
} | |
* { width: 100%; | |
height: 100%; | |
margin: 0px; | |
} | |
canvas { | |
width:100%; | |
display:block; | |
margin:auto; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment