132 lines
3.5 KiB
HTML
132 lines
3.5 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html>
|
||
|
<head>
|
||
|
<meta charset="utf-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||
|
<title>tap.clerie.de</title>
|
||
|
<style>
|
||
|
body {
|
||
|
background-color: black;
|
||
|
color: lightgray;
|
||
|
}
|
||
|
#canvas {
|
||
|
width: 100%;
|
||
|
height: 100%;
|
||
|
display: block;
|
||
|
position: fixed;
|
||
|
top: 0;
|
||
|
left: 0;
|
||
|
z-index: -9999;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
<h1>tap.clerie.de</h1>
|
||
|
<canvas id="canvas" width="500" height="50"></canvas>
|
||
|
<h2 id="bpm">_ bpm</h2>
|
||
|
<p><small>Went offbeat? <a href="https://git.clerie.de/clerie/tap.clerie.de">Host yourself :3</a></small></p>
|
||
|
|
||
|
<script>
|
||
|
let cv = document.getElementById("canvas");
|
||
|
cv.width = window.innerWidth;
|
||
|
cv.height = window.innerHeight;
|
||
|
let ctx = canvas.getContext('2d');
|
||
|
let zoom = 10;
|
||
|
|
||
|
function resizeCanvas() {
|
||
|
cv.width = window.innerWidth;
|
||
|
cv.height = window.innerHeight;
|
||
|
}
|
||
|
|
||
|
window.onresize = resizeCanvas;
|
||
|
|
||
|
let bpmField = document.getElementById("bpm");
|
||
|
|
||
|
function getColorHue(slot) {
|
||
|
return 13 * slot;
|
||
|
};
|
||
|
|
||
|
function getColor(slot) {
|
||
|
return "hsl(" + getColorHue(slot) + ", 100%, 50%)";
|
||
|
}
|
||
|
|
||
|
function getMutedColor(slot) {
|
||
|
return "hsl(" + getColorHue(slot) + ", 50%, 50%)";
|
||
|
}
|
||
|
|
||
|
let tap_buffer = [];
|
||
|
|
||
|
function getBpm(slot, pos, length) {
|
||
|
if (length > tap_buffer[slot].length) {
|
||
|
length = tap_buffer[slot].length;
|
||
|
}
|
||
|
if (length < 2) {
|
||
|
return 0;
|
||
|
}
|
||
|
return Math.round(length / ((tap_buffer[slot].at(pos) - tap_buffer[slot].at(pos - (length-1))) / 60000));
|
||
|
}
|
||
|
|
||
|
function plotGraph(currentTime, slot, len) {
|
||
|
ctx.beginPath();
|
||
|
let last_x = cv.width;
|
||
|
let last_y = cv.height - getBpm(slot, -1, len);
|
||
|
ctx.moveTo(last_x, last_y);
|
||
|
for (let i = -1; i > -(tap_buffer[slot].length-1); i--) {i
|
||
|
let cur_x = cv.width - ((currentTime - tap_buffer[slot].at(i)) / zoom);
|
||
|
let cur_y = cv.height - getBpm(slot, i, len);
|
||
|
let dist = Math.round((last_x - cur_x)/4);
|
||
|
ctx.bezierCurveTo(last_x - dist, last_y, cur_x + dist, cur_y, cur_x, cur_y);
|
||
|
|
||
|
last_x = cur_x;
|
||
|
last_y = cur_y;
|
||
|
|
||
|
if (cv.width - ((currentTime - tap_buffer[slot].at(i)) / zoom) < 0) {
|
||
|
break;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
ctx.stroke();
|
||
|
}
|
||
|
|
||
|
function plotLoop() {
|
||
|
let currentTime = Date.now();
|
||
|
ctx.clearRect(0, 0, cv.width, cv.height);
|
||
|
tap_buffer.forEach((_, slot) => {
|
||
|
ctx.strokeStyle = getMutedColor(slot);
|
||
|
ctx.lineWidth = 1;
|
||
|
plotGraph(currentTime, slot, 3);
|
||
|
ctx.strokeStyle = getMutedColor(slot);
|
||
|
ctx.lineWidth = 2;
|
||
|
plotGraph(currentTime, slot, 25);
|
||
|
ctx.strokeStyle = getColor(slot);
|
||
|
ctx.lineWidth = 4;
|
||
|
plotGraph(currentTime, slot, 10);
|
||
|
});
|
||
|
window.requestAnimationFrame(plotLoop);
|
||
|
}
|
||
|
|
||
|
function handleClick(slot) {
|
||
|
if (tap_buffer[slot] === undefined) {
|
||
|
tap_buffer[slot] = [];
|
||
|
}
|
||
|
tap_buffer[slot].push(Date.now());
|
||
|
let out = "";
|
||
|
tap_buffer.forEach((_, slot) => {
|
||
|
out += '<span style="color:' + getColor(slot) + ';">' + getBpm(slot, -1, 10) + " bpm</span><br>";
|
||
|
});
|
||
|
bpmField.innerHTML = out;
|
||
|
}
|
||
|
|
||
|
window.addEventListener("keydown", (e) => {
|
||
|
let slot = "abcdefghijklmnopqrstuvwxyz".indexOf(e.key[0]) + 1;
|
||
|
handleClick(slot);
|
||
|
});
|
||
|
window.addEventListener("mousedown", (e) => {
|
||
|
handleClick(22);
|
||
|
});
|
||
|
|
||
|
window.requestAnimationFrame(plotLoop);
|
||
|
</script>
|
||
|
</body>
|
||
|
</html>
|