Created
November 15, 2022 23:38
-
-
Save joeskeen/1f998a804b08e94aaacadfd092acb34f to your computer and use it in GitHub Desktop.
A vanilla HTML/CSS/JS implementation of rendering your webcam as ASCII art on a web page (and ability to turn that into a virtual webcam for video calls)
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 lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>ASCII Webcam</title> | |
<style type="text/css"> | |
html, | |
body { | |
background-color: black; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
overflow: hidden; | |
max-width: 100vw; | |
max-height: 100vh; | |
} | |
video.original-video { | |
display: none; | |
} | |
div.ascii-video { | |
white-space: pre; | |
font-family: monospace; | |
font-size: 9px; | |
line-height: 8px; | |
color: green; | |
margin-left: auto; | |
margin-right: auto; | |
width: auto; | |
} | |
canvas.scaled-video { | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
<video class="original-video"></video> | |
<canvas class="scaled-video"></canvas> | |
<div class="ascii-video"></div> | |
<script type="text/javascript"> | |
(async () => { | |
const green = { r: 100, g: 130, b: 100 }; | |
const frameRate = 10; | |
const scaleWidth = 200; | |
const asciiCharsDarkToLight = " .-,':¹;²!=<71ua%&ߨ"; | |
const interval = Math.floor(1000 / frameRate); | |
const videoElement = document.querySelector("video.original-video"); | |
const scaledVideo = document.querySelector("canvas.scaled-video"); | |
const asciiVideo = document.querySelector("div.ascii-video"); | |
const loadPromise = new Promise((resolve) => { | |
videoElement.onloadeddata = resolve; | |
}); | |
const stream = await navigator.mediaDevices.getUserMedia({ | |
video: true, | |
}); | |
videoElement.srcObject = stream; | |
videoElement.play(); | |
await loadPromise; | |
const { videoWidth, videoHeight } = videoElement; | |
const scaleHeight = Math.floor((videoHeight * scaleWidth) / videoWidth); | |
console.log({ videoWidth, videoHeight, scaleWidth, scaleHeight }); | |
scaledVideo.width = scaleWidth; | |
scaledVideo.height = scaleHeight; | |
const ctx = scaledVideo.getContext("2d", { willReadFrequently: true }); | |
draw(); | |
function draw() { | |
ctx.drawImage(videoElement, 0, 0, scaleWidth, scaleHeight); | |
const imageData = ctx.getImageData( | |
0, | |
0, | |
scaleWidth, | |
scaleHeight | |
).data; | |
const ascii = []; | |
for (let i = 0; i < scaleWidth * scaleHeight * 4; i += 4) { | |
let r = imageData[i]; | |
let g = imageData[i + 1]; | |
let b = imageData[i + 2]; | |
let a = imageData[i + 3]; | |
if (i === 48) { | |
// console.log(`rgba(${r},${g},${b},${a})`); | |
} | |
// filter out green screen pixels using euclidian distance | |
const distance = Math.sqrt( | |
Math.pow(r - green.r, 2) + | |
Math.pow(g - green.g, 2) + | |
Math.pow(b - green.b, 2) | |
); | |
if (distance < 95) { | |
r = 0; | |
g = 0; | |
b = 0; | |
} | |
const brightness = (r + g + b) / 3; | |
const index = Math.floor( | |
(brightness / 255) * asciiCharsDarkToLight.length | |
); | |
const char = asciiCharsDarkToLight[index]; | |
ascii.push(char); | |
if (i % (scaleWidth * 4) === 0) { | |
ascii.push("\n"); | |
} | |
} | |
asciiVideo.textContent = ascii.join(""); | |
setTimeout(draw, interval); | |
} | |
})(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
ASCII Art Webcam
Thanks to The Coding Train on YouTube for the P5.js tutorial upon which this is based! https://youtu.be/55iwMYv8tGI
Prerequisites
Running in the Browser
index.html
npx http-server .
. This tells Node to pull down the packagehttp-server
and run it in the current directory. If this works, you will get output similar to this:Use the ASCII version of yourself in video meetings
npx http-server .
command from above running in the background--use-fake-ui-for-media-stream
. This allows OBS to auto-accept the permission to microphone and webcam in Browser sourceshttp://127.0.0.1
. Set the width and height to 1920 and 1080, respectively.Customization and Notes
This attempts to detect a green screen behind you and remove it. If you don't have a green screen, or your green screen is a different color than mine, you can delete or make adjustments to lines 44, 91, and/or 95-104.
If you want to target a specific webcam, you can specify it on line 59. See
getUserMedia
documentation on MDN for information on getting media device IDs.If you want to change the "resolution" of the ASCII art, change lines 46 and 25-26.
To change the number of frames per second, change line 45.
To change the background or foreground colors, change lines 11 and/or 27.