Skip to content

Instantly share code, notes, and snippets.

@atoponce
Last active June 25, 2021 22:06
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 atoponce/0dcb78af617d30d335a0caaea378df7e to your computer and use it in GitHub Desktop.
Save atoponce/0dcb78af617d30d335a0caaea378df7e to your computer and use it in GitHub Desktop.
Mouse entropy in the browser using JavaScript
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
<title>Mouse entropy</title>
<link rel='shortcut icon' href='data:image/x-icon;base64,AAABAAEAFB8QAAEABAA8AgAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAUAAAAHwgGAAAA507tzgAAAgNJREFUSImtlr9PwkAUx781jqSbSHTQyKC7pn/AheimEBKNJgwMrCxE7FA3hgbTxDDiwMAICZtsXTUNTsw31IQEBw0pJLLhYO68/gAK9Zs0udd3/eR7L/fuKu0f7M3wJwkemabJx4QQX+zVhiee+WasKA7M5/P/AuXAXC6HYrEYGepacjqdhqZpkaDeGoIQgupDFbIsrwX1AQHg5PgE2r0GWZZBCFkJGghkUMMwVobOBQJAMpmEYRhIbCcYdCl4IZBBH2uPSGwnojtkim/FUX+qc6eLShAKCACxWAz1pzqODo+iOxSh1YcqFEURa+pyuxKQQXVdh6Io0R2K0nUdp6dnLORO1wYCgKreiVBEBjKoeFJthv2QUop+v4/xeIzPry9Mv6d4t22MRiMMP4Z8Xmigbduo1WrLpklzl0wphaqqPCaEeLtFCniCa0gpRalUgmVZ6L31+PvLq8tlDv3A3lsPhUIBjuMAANqtNs+lUil2ToYDmqaJ8m3ZNcGyLFBKAfxu6mw2y1KB/cyBzWYTlUpFzPErtSW4zGQy4Rw2Go0gmAQAr68vmEwm3OX5xcVCoHi5Swi47B3HQafT4fHNzTUb+pbNHAaCRHWfu3wc34p7+9gHXCQJAIYfQ9dvyO7ujivPFLpTgN8tNBgM0H3uiu02E6HS/sFeWJ63XoElWve0mVvvH79Op6Tbn+HjAAAAAElFTkSuQmCC' />
<style>
body {
text-align: center;
}
#randogram {
cursor: crosshair;
touch-action: none;
}
#container {
margin: 0 auto;
pointer-events: auto;
width: 512px;
}
#progress {
background: #dddddd;
width: 512px;
}
#progressBar {
background: #0088FF;
color: #ffffff;
font-weight: bold;
height: 40px;
line-height: 40px;
max-width: 512px;
text-align: center;
width: 0%;
}
#entropyResult {
font-family: monospace;
height: 40px;
width: 512px;
}
</style>
</head>
<body>
<div id='container'>
<h1>JavaScript Mouse Entropy</h1>
<p>Download this page to your computer, and run it offline.<br/>Move your mouse over the randogram below until the progress meter is 100%.</p>
<canvas id='randogram' width='512' height='512'></canvas>
<div id='progress'>
<div id='progressBar'></div>
</div>
<p id='entropyResult'></p>
</div>
<script>
// https://exploringjs.com/impatient-js/ch_typed-arrays.html#concatenating-typed-arrays
function concatenate(resultConstructor, ...arrays) {
let totalLength = 0
for (const arr of arrays) totalLength += arr.length
const result = new resultConstructor(totalLength)
let offset = 0
for (const arr of arrays) {
result.set(arr, offset)
offset += arr.length
}
return result
}
function randogram() {
return crypto.getRandomValues(new Uint8Array(65536)) // max ArrayBufferView byte length
}
// https://stackoverflow.com/a/41550641
function bin2hex(b) {
return b.match(/.{4}/g).reduce(function(acc, i) {
return acc + parseInt(i, 2).toString(16)
}, '')
}
function sleep(ms) {
const date = Date.now()
let currentDate = null
do currentDate = Date.now()
while (currentDate - date < ms)
}
function genPixels() {
return concatenate(Uint8Array, randogram(), randogram(), randogram(), randogram())
}
function drawRandogram() {
const canvas = document.getElementById('randogram')
const ctx = canvas.getContext('2d', {alpha: false})
const imgData = ctx.getImageData(0, 0, 512, 512)
const pixels = genPixels()
for (let i = 0; i < imgData.data.length; i += 4) {
if (pixels[i / 4] % 2 === 0) {
imgData.data[i] = 0
imgData.data[i + 1] = 136
imgData.data[i + 2] = 255
} else {
imgData.data[i] = 255
imgData.data[i + 1] = 255
imgData.data[i + 2] = 255
}
}
ctx.putImageData(imgData, 0, 0)
requestAnimationFrame(drawRandogram)
return pixels
}
function getEntropy() {
const progressBar = document.getElementById('progressBar')
const entropyResult = document.getElementById('entropyResult')
const pixels = drawRandogram()
let entropy = ""
let neumann = []
document.getElementById('randogram').onpointermove = function(e) {
e.preventDefault()
if (entropy.length < 256) {
const x = Math.floor(e.offsetX)
const y = Math.floor(e.offsetY)
if (0 <= x && x < 512 && 0 <= y && y < 512) {
const p = 512 * y + x
let progress = 0
neumann.push(pixels[p] % 2)
// john von neumann randomness extractor
if (neumann.length === 2) {
if (neumann[0] !== neumann[1]) entropy += neumann[0]
neumann = []
}
progress = Math.floor(entropy.length/256 * 100)
if (progress > 100) progress = 100
else {
progressBar.style.width = progress + "%"
progressBar.innerText = progress + "%"
} // if progress > 100
} // if 0 <= x < 512 && 0 <= y < 512
} else {
entropyResult.innerText = bin2hex(entropy)
} // if entropy.length < 256
} // onpointermove
requestAnimationFrame(drawRandogram)
} // getEntropy()
getEntropy()
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment