Last active
September 29, 2023 00:41
-
-
Save kubarskii/5bf43f65799d858e6b612cc8b1f76d2a to your computer and use it in GitHub Desktop.
Render video as ascii
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: #00005e | |
} | |
video, canvas { | |
/*display: none;*/ | |
} | |
#text-video { | |
font-family: Courier,serif; | |
font-size: 6px; | |
line-height: 4px; | |
color: white; | |
} | |
</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 gradient = "_______.:!/r(l1Z4H9W8$@"; | |
const preparedGradient = gradient.replaceAll('_', '\u00A0') | |
const rainbowColors = [ | |
'#9400D3', | |
'#4B0082', | |
'#0000FF', | |
'#00FF00', | |
'#FFFF00', | |
'#FF7F00', | |
'#FF0000' | |
] | |
const randomRainbowColor = () => { | |
const i = Math.floor(Math.random() * rainbowColors.length) | |
return rainbowColors[i] | |
} | |
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 getCharByScale = (scale) => { | |
const val = Math.floor(scale / 255 * (gradient.length - 1)) | |
return preparedGradient[val] | |
} | |
const renderText = (node, textDarkScale) => { | |
let txt = `<div>` | |
for (let i = 0; i < textDarkScale.length; i++) { | |
txt += `<span style="color: ${randomRainbowColor()}">` | |
for (let k = 0; k < textDarkScale[i].length; k++) { | |
txt = `${txt}${getCharByScale(textDarkScale[i][k])}` | |
} | |
txt += `</span><br>` | |
} | |
txt += `</div>` | |
node.innerHTML = txt | |
} | |
let running = true; | |
init() | |
const frame = () => requestAnimationFrame(() => { | |
render(ctx) | |
const chars = getPixelsGreyScale(ctx) | |
renderText(textVideo, chars) | |
if (running) | |
frame() | |
}) | |
frame() | |
document.getElementById('stop').addEventListener('click', (e) => { | |
running = false | |
}) | |
})() | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment