const states = { GETKEYS: 'getkeys', TRILLING: 'trilling', READY: 'ready', RESULT: 'result' } const colors = { GETKEYS: 'gray', TRILLING: 'crimson', READY: 'darkgray', RESULT: 'dodgerblue' } var state = states.GETKEYS; var times = [] const max_count = 20; var key1, key2; var time_start; var counter = 0; function handle_press(e) { switch (state) { case states.GETKEYS: { if (!key1) { key1 = e.code; area.style["background-color"] = colors.GETKEYS; area.innerText = "Press 2nd key!"; } else { key2 = e.code; state = states.READY; area.style["background-color"] = colors.READY; area.innerText = "Test will start on first keypress."; } } break; case states.READY: { if (e.code == key1 || e.code == key2) { state = states.TRILLING; time_start = Date.now(); area.style["background-color"] = colors.TRILLING; handle_press(e); // update couter/text for first keypress } } break; case states.TRILLING: { if (e.code == key1 || e.code == key2) { counter++; } area.innerText = counter + "/" + max_count; if (counter == max_count) { state = states.RESULT; counter = 0; let time = parseInt((max_count / ((Date.now() - time_start) / 1000) * 60 / 4)); times.push(time); let average = times.reduce((a, b) => a + b) / times.length; document.getElementById("average").innerText = "Average: " + average + "bpm"; var li = document.createElement("li"); li.appendChild(document.createTextNode(time + "bpm")); document.getElementById("times").appendChild(li); area.style["background-color"] = colors.RESULT; area.innerText = time + "bpm" + '\n(press space to try again)'; } } break; case states.RESULT: { if (e.code == "Space") { state = states.READY; area.style["background-color"] = colors.READY; area.innerText = "Test will start on first keypress."; } } break; default: break } } window.onload = function () { create_prologue(); area = document.getElementById("area"); area.innerText = "Press 1st key!"; document.addEventListener('keydown', handle_press); }