Skip to content

Instantly share code, notes, and snippets.

@kubarskii
Last active September 29, 2023 00:41
Show Gist options
  • Save kubarskii/5bf43f65799d858e6b612cc8b1f76d2a to your computer and use it in GitHub Desktop.
Save kubarskii/5bf43f65799d858e6b612cc8b1f76d2a to your computer and use it in GitHub Desktop.
Render video as ascii
<!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