Last active
December 21, 2016 21:38
-
-
Save njvack/920dac2791b2d100b25e477775bc50bf to your computer and use it in GitHub Desktop.
Blinken
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8" /> | |
<script type="text/javascript"> | |
window.console = window.console || {} | |
window.console.log = window.console.log || function() {}; | |
console.log("Hello"); | |
// Standard sum function | |
function sum(ar) { | |
return ar.reduce(function(s, val) { | |
return s + val; | |
}, 0) | |
} | |
// Numerical mean | |
function mean(ar) { | |
return sum(ar) / ar.length; | |
} | |
function avg_square_diffs(ar) { | |
var m = mean(ar); | |
var sqdiffs = ar.map(function(e) { | |
var d = e - m; | |
return d * d; | |
}); | |
return mean(sqdiffs); | |
} | |
// Yup, it's standard deviation | |
function stdev(ar) { | |
return Math.sqrt(avg_square_diffs(ar)); | |
} | |
var blink_controller = function(cycle_ms, blink_ms, blink_element, record_blinks) { | |
var my = {}; | |
my.cycle_ms = cycle_ms; | |
my.blink_ms = blink_ms; | |
my.blink_element = blink_element; | |
my.record_blinks = record_blinks; | |
my.blinks_recorded = 0; | |
my.event_list = []; | |
my.keep_blinking = false; | |
my.started = false; | |
my.finished = false; | |
function blink_on() { | |
my.blink_element.classList.add("on"); | |
if (my.started) { | |
my.blinks_recorded += 1; | |
console.log(my.blinks_recorded); | |
} | |
} | |
function blink_off() { | |
my.blink_element.classList.remove("on"); | |
if (my.blinks_recorded >= my.record_blinks) { | |
my.finished = true; | |
my.stop_blinking(); | |
var evt = new Event('finish'); | |
my.blink_element.dispatchEvent(evt); | |
} | |
} | |
function record_blink(timestamp) { | |
my.event_list.push({ | |
"type": "blink_on", | |
"timestamp": timestamp, | |
}); | |
} | |
var handle_frame = function(timestamp) { | |
// console.log("in handle_frame...") | |
if (my.keep_blinking) { | |
my.last_frame_time = my.last_frame_time || timestamp; | |
var elapsed = timestamp - my.last_frame_time; | |
// console.log([elapsed, my.blink_remaining, my.cycle_remaining].join("\t")); | |
if (isFinite(my.blink_remaining)) { | |
my.blink_remaining -= elapsed; | |
} | |
if (my.blink_remaining <= 0) { | |
blink_off(); | |
my.blink_remaining = NaN; | |
//my.event_list.push(["blink_off", timestamp]) | |
console.log("blink_off\t"+timestamp); | |
} | |
my.cycle_remaining -= elapsed; | |
if (my.cycle_remaining <= 0 ) { | |
blink_on(); | |
my.blink_remaining = my.blink_ms; | |
my.cycle_remaining = my.cycle_ms; | |
my.event_list.push( | |
{ | |
"type": "blink_on", | |
"timestamp": timestamp | |
}); | |
console.log("blink_on\t"+timestamp); | |
} | |
my.last_frame_time = timestamp; | |
if (my.keep_blinking) { | |
window.requestAnimationFrame(handle_frame); | |
} | |
} | |
} | |
my.start_blinking = function() { | |
my.keep_blinking = true; | |
my.cycle_remaining = my.cycle_ms; | |
my.blink_remaining = my.blink_ms; | |
window.requestAnimationFrame(handle_frame); | |
} | |
my.stop_blinking = function() { | |
my.keep_blinking = false; | |
} | |
function make_sort_function(evt) { | |
return function(ke1, ke2) { | |
var dist1 = Math.abs(evt.timestamp - ke1.timestamp); | |
var dist2 = Math.abs(evt.timestamp - ke2.timestamp); | |
return dist1 - dist2; | |
}; | |
} | |
function get_blink_events() { | |
return my.event_list.filter(function(e) { return e.type == 'blink_on'; }); | |
} | |
function get_press_events() { | |
return my.event_list.filter(function(e) { return e.type == 'keypress'; }); | |
} | |
my.match_blinks_and_presses = function() { | |
var blinks = get_blink_events(); | |
var bs = blinks.slice(); | |
var presses = get_press_events(); | |
console.log(presses); | |
presses.forEach(function(p) { | |
console.log("Finding closest blink..."); | |
console.log(p); | |
var sort_fx = make_sort_function(p); | |
bs.sort(sort_fx); | |
console.log(p); | |
var b = bs[0]; | |
console.log("Closest match:"); | |
console.log([p.timestamp, b.timestamp]); | |
b.press_candidate = b.press_candidate || p; | |
var old_diff = Math.abs(b.timestamp, b.press_candidate.timestamp); | |
var new_diff = Math.abs(b.timestamp, p.timestamp); | |
if (new_diff < old_diff) { | |
b.press_candidate = p; | |
} | |
}); | |
console.log(blinks); | |
return blinks.filter(function(b) { return b.press_candidate; }).map(function(b) { | |
return { | |
"blink": b, | |
"press": b.press_candidate, | |
"diff": b.press_candidate.timestamp - b.timestamp | |
} | |
}); | |
} | |
function record_press(timestamp) { | |
my.event_list.push({ | |
"type": "keypress", | |
"timestamp": timestamp | |
}); | |
} | |
my.handle_press = function(event) { | |
console.log(event); | |
if (my.finished) { | |
return; | |
} | |
if (!my.started) { | |
console.log("starting to record"); | |
my.started = true; | |
} | |
record_press(window.performance.now()); | |
} | |
return my; | |
} | |
window.addEventListener('load', function() { | |
console.log("Loaded"); | |
var blink_elt = document.getElementById('blinker'); | |
var record_blinks = 14; | |
var blinks_per_minute = 60; | |
var cycle_ms = (60 * 1000) / blinks_per_minute; | |
var blink_ms = (6/60) * 1000; | |
window.bc = blink_controller(cycle_ms, blink_ms, blink_elt, record_blinks); | |
document.addEventListener('keydown', bc.handle_press); | |
blink_elt.addEventListener('finish', function(e) { | |
var res = bc.match_blinks_and_presses(); | |
var diffs = res.map(function(thing) { return thing.diff; }); | |
document.getElementById("results").innerHTML = diffs.join("\n"); | |
var m = mean(diffs); | |
var s = stdev(diffs); | |
document.getElementById("mean").innerHTML = "Mean: " + m; | |
document.getElementById("std").innerHTML = "Stdev: " + s; | |
}); | |
bc.start_blinking(); | |
}); | |
</script> | |
<style type="text/css"> | |
#blinker { | |
width: 200px; | |
height: 200px; | |
border-radius: 100px; | |
margin: 32px; | |
} | |
#blinker.on { | |
background-color: red; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="blinker" class="on"> </div> | |
<p>Press space when it blinks!</p> | |
<pre><div id="results"></div></pre> | |
<h3 id="mean"></h3> | |
<h3 id="std"></h3> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment