Skip to content

Instantly share code, notes, and snippets.

@joeskeen
Created November 15, 2022 23:38
Show Gist options
  • Save joeskeen/1f998a804b08e94aaacadfd092acb34f to your computer and use it in GitHub Desktop.
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)
<!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>
@joeskeen
Copy link
Author

joeskeen commented Nov 15, 2022

ASCII Art Webcam

image

Thanks to The Coding Train on YouTube for the P5.js tutorial upon which this is based! https://youtu.be/55iwMYv8tGI

Prerequisites

  • OBS Studio
  • NodeJS
  • A webcam

Running in the Browser

  • Save the HTML file locally as index.html
  • Open a command prompt/terminal window to the directory you saved it to
  • Run npx http-server .. This tells Node to pull down the package http-server and run it in the current directory. If this works, you will get output similar to this:
    Starting up http-server, serving .
    
    http-server version: 14.1.1
    
    http-server settings: 
    CORS: disabled
    Cache: 3600 seconds
    Connection Timeout: 120 seconds
    Directory Listings: visible
    AutoIndex: visible
    Serve GZIP Files: false
    Serve Brotli Files: false
    Default File Extension: none
    
    Available on:
      http://127.0.0.1:8080
      http://192.168.0.192:8080
    Hit CTRL-C to stop the server
    
  • Go to http://127.0.0.1 to see yourself in ASCII!

Use the ASCII version of yourself in video meetings

  • Have the npx http-server . command from above running in the background
  • Run OBS with the extra command-line argument --use-fake-ui-for-media-stream. This allows OBS to auto-accept the permission to microphone and webcam in Browser sources
  • In OBS, add a Browser source. Configure its URL to http://127.0.0.1. Set the width and height to 1920 and 1080, respectively.
  • If everything is running and configured correctly, you should see yourself in OBS.
  • On the bottom-right of OBS, click Start Virtual Webcam
  • Join your video call. Configure your video settings to use the "OBS Virtual Webcam" webcam.
  • People should now be able to see you as ASCII!

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment