-
-
Save connordavenport/896a4c61234bf1166ba8c632917333dd to your computer and use it in GitHub Desktop.
Render video as ascii — use a variable font instead of ascii characters
This file contains 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"> | |
<meta name="viewport" | |
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Document</title> | |
</head> | |
<style> | |
body { | |
background: #ffffff | |
} | |
video, canvas { | |
display: none; | |
} | |
@font-face { | |
font-family: 'SourceCode'; | |
src: url('SourceCodeVariable-Roman.ttf') format('truetype-variations'); | |
/*font-weight: 0 1000;*/ | |
} | |
#text-video { | |
font-family: "SourceCode", sans-serif; | |
font-weight: 0; | |
font-size: 10px; | |
line-height: 8px; | |
color: black; | |
} | |
</style> | |
<body> | |
<div> | |
<div> | |
<video id="video">Video stream not available.</video> | |
<canvas id="canvas-video"></canvas> | |
</div> | |
<div id="text-video"></div> | |
<button id="stop">Stop</button> | |
</div> | |
</body> | |
<script> | |
(function () { | |
const video = document.getElementById('video') | |
const textVideo = document.getElementById('text-video') | |
const canvas = document.getElementById('canvas-video') | |
const ctx = canvas.getContext('2d'); | |
const width = 320 / 2, height = 240 / 2; | |
const init = () => { | |
navigator.mediaDevices.getUserMedia({ video: true, audio: false }) | |
.then(function (stream) { | |
video.srcObject = stream; | |
video.play(); | |
}) | |
.catch(function (err) { | |
console.log("An error occurred: " + err); | |
}); | |
} | |
const clearphoto = () => { | |
ctx.fillStyle = "#fff"; | |
ctx.fillRect(0, 0, width, height); | |
} | |
const render = (ctx) => { | |
if (width && height) { | |
canvas.width = width; | |
canvas.height = height; | |
ctx.drawImage(video, 0, 0, width, height); | |
} else { | |
clearphoto(); | |
} | |
} | |
const getPixelsGreyScale = (ctx) => { | |
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
const data = imageData.data; | |
let row = 0 | |
const res = new Array(height).fill(0).map(() => []); | |
for (let i = 0, c = 0; i < data.length; i += 4) { | |
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3; | |
let curr = res[row] | |
curr.push(avg) | |
if (c < width) { | |
c++ | |
} | |
if (c === width) { | |
c = 0 | |
row += 1 | |
} | |
} | |
return res | |
} | |
const renderText = (node, textDarkScale) => { | |
let txt = `<div>` | |
for (let i = 0; i < textDarkScale.length; i++) { | |
for (let k = 0; k < textDarkScale[i].length; k++) { | |
txt = `${txt}<span style=\"font-variation-settings: \'wght\' ${Math.floor((1 - (textDarkScale[i][k]/255)) * 1000)};\">a</span>` | |
} | |
txt += `<br>` | |
} | |
txt += `</div>` | |
node.innerHTML = txt | |
} | |
init() | |
const interval = setInterval(() => { | |
requestAnimationFrame(() => { | |
render(ctx) | |
const chars = getPixelsGreyScale(ctx) | |
renderText(textVideo, chars) | |
}) | |
}) | |
document.getElementById('stop').addEventListener('click', (e) => { | |
clearInterval(interval) | |
}) | |
})() | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment