Last active
February 26, 2025 14:54
-
-
Save egstad/1f3d1b6f80d43d9295170e1fcf2b74d0 to your computer and use it in GitHub Desktop.
GitHub Shop: ASCII Effect
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
// __ ___ ___ ____ ____ ___ __ _ _ _ _ __ ___ | |
// /__\ / __)/ __(_ _(_ _) / __) /__\ ( \( ( \/ /__\ / __) | |
// /(__)\\__ ( (__ _)(_ _)(_ ( (__ /(__)\ ) ( \ /(__)\\__ \ | |
// (__)(__(___/\___(____(____) \___(__)(__(_)\_) \(__)(__(___/ | |
// What you'll need... | |
// 1. A canvas: <canvas id="asciiCanvas"></canvas> | |
// 2. An image: <img id="image" src="" alt=""> | |
// Fetch elements | |
const canvas = document.querySelector('#asciiCanvas'); | |
const img = document.querySelector('#image'); | |
// Load the image from wherever then draw to canvas | |
img.crossOrigin = 'Anonymous'; | |
img.onload = () => drawAsciiArt(canvas, img, 32); | |
// Redraw on mouse move | |
window.addEventListener('mousemove', redrawAscii) | |
function drawAsciiArt(canvasID, image, numCols = 16) { | |
const asciiChars = "@%#*+=-. "; // chars used in ASCII (dark to light) | |
const aspectRatio = img.width / img.height; | |
const charWidth = img.width / numCols; | |
const charHeight = charWidth * (aspectRatio * 2.08); // adjust this number accordingly | |
const numRows = Math.floor(img.height / charHeight); | |
const adjustedNumCols = Math.floor(img.width / charWidth); | |
const adjustedNumRows = Math.floor(img.height / charHeight); | |
const ctx = canvas.getContext("2d"); | |
// setup canvas dimensions | |
canvas.width = img.width; | |
canvas.height = img.height; | |
// clear canvas before drawing | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.font = `${charHeight}px monospace`; | |
ctx.fillStyle = "black"; | |
// draw the image directly on the main canvas | |
ctx.drawImage(img, 0, 0, adjustedNumCols, adjustedNumRows); | |
// get image data from the main canvas | |
const imageData = ctx.getImageData( | |
0, | |
0, | |
adjustedNumCols, | |
adjustedNumRows | |
).data; | |
let asciiArt = ""; | |
for (let y = 0; y < adjustedNumRows; y++) { | |
for (let x = 0; x < adjustedNumCols; x++) { | |
const offset = (y * adjustedNumCols + x) * 4; | |
const r = imageData[offset]; | |
const g = imageData[offset + 1]; | |
const b = imageData[offset + 2]; | |
const avg = (r + g + b) / 3; | |
const charIndex = Math.floor((avg / 255) * (asciiChars.length - 1)); | |
asciiArt += asciiChars[charIndex]; | |
} | |
asciiArt += "\n"; | |
} | |
// Clear the canvas again before drawing ASCII art | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
// Draw ASCII art on the main canvas | |
const asciiLines = asciiArt.split("\n"); | |
asciiLines.forEach((line, index) => { | |
ctx.fillText(line, 0, charHeight * (index + 1)); | |
}); | |
} | |
function range(value, minInput, maxInput, minOutput = 4, maxOutput = 128) { | |
value = Math.max(minInput, Math.min(maxInput, value)); | |
return Math.ceil(minOutput + ((value - minInput) * (maxOutput - minOutput)) / (maxInput - minInput)); | |
} | |
function redrawAscii(ev) { | |
const mappedValue = range(ev.x, 0, window.innerWidth); | |
drawAsciiArt('#asciiCanvas', '#image', mappedValue); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment