Skip to content

Instantly share code, notes, and snippets.

@egstad
Last active February 26, 2025 14:54
Show Gist options
  • Save egstad/1f3d1b6f80d43d9295170e1fcf2b74d0 to your computer and use it in GitHub Desktop.
Save egstad/1f3d1b6f80d43d9295170e1fcf2b74d0 to your computer and use it in GitHub Desktop.
GitHub Shop: ASCII Effect
// __ ___ ___ ____ ____ ___ __ _ _ _ _ __ ___
// /__\ / __)/ __(_ _(_ _) / __) /__\ ( \( ( \/ /__\ / __)
// /(__)\\__ ( (__ _)(_ _)(_ ( (__ /(__)\ ) ( \ /(__)\\__ \
// (__)(__(___/\___(____(____) \___(__)(__(_)\_) \(__)(__(___/
// 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