You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, targetWidth = 80, character = "█") {
// Standard 16-color ANSI palette (based on common xterm/modern terminal colors)
const modernAnsiPalette = [
{ name: "Black", r: 0, g: 0, b: 0, code: "\x1b[30m" },
{ name: "Red", r: 205, g: 0, b: 0, code: "\x1b[31m" },
{ name: "Green", r: 0, g: 205, b: 0, code: "\x1b[32m" },
{ name: "Yellow", r: 205, g: 205, b: 0, code: "\x1b[33m" },
{ name: "Blue", r: 0, g: 0, b: 238, code: "\x1b[34m" },
{ name: "Magenta", r: 205, g: 0, b: 205, code: "\x1b[35m" },
{ name: "Cyan", r: 0, g: 205, b: 205, code: "\x1b[36m" },
{ name: "White", r: 229, g: 229, b: 229, code: "\x1b[37m" },
{ name: "BrightBlack", r: 127, g: 127, b: 127, code: "\x1b[90m" }, // Also known as Gray
{ name: "BrightRed", r: 255, g: 0, b: 0, code: "\x1b[91m" },
{ name: "BrightGreen", r: 0, g: 255, b: 0, code: "\x1b[92m" },
{ name: "BrightYellow", r: 255, g: 255, b: 0, code: "\x1b[93m" },
{ name: "BrightBlue", r: 92, g: 92, b: 255, code: "\x1b[94m" },
{ name: "BrightMagenta", r: 255, g: 0, b: 255, code: "\x1b[95m" },
{ name: "BrightCyan", r: 0, g: 255, b: 255, code: "\x1b[96m" },
{ name: "BrightWhite", r: 255, g: 255, b: 255, code: "\x1b[97m" }
];
// Helper function to calculate squared Euclidean distance between_Tool colors
function colorDistanceSquared(r1, g1, b1, r2, g2, b2) {
const dr = r1 - r2;
const dg = g1 - g2;
const db = b1 - b2;
return dr * dr + dg * dg + db * db;
}
// Helper function to find the closest ANSI color in the palette
function findClosestAnsiColor(r, g, b, palette) {
let closestColor = palette[0];
let minDistance = colorDistanceSquared(r, g, b, closestColor.r, closestColor.g, closestColor.b);
for (let i = 1; i < palette.length; i++) {
const currentColor = palette[i];
const distance = colorDistanceSquared(r, g, b, currentColor.r, currentColor.g, currentColor.b);
if (distance < minDistance) {
minDistance = distance;
closestColor = currentColor;
}
}
return closestColor.code;
}
// Dynamically load AnsiUp library for converting ANSI codes to HTML
// AnsiUp is a UMD module, so it might define a global `AnsiUp` or be usable with `import()`.
// This method focuses on global `AnsiUp`.
if (typeof AnsiUp === 'undefined') {
const scriptId = 'ansi_up_script_loader'; // Unique ID for the script tag
const ansiUpUrl = 'https://cdn.jsdelivr.net/npm/ansi_up@5.2.1/ansi_up.min.js';
if (!document.getElementById(scriptId)) {
const script = document.createElement('script');
script.id = scriptId;
script.src = ansiUpUrl;
script.type = 'text/javascript';
script.async = true;
const loadPromise = new Promise((resolve, reject) => {
script.onload = () => {
if (typeof AnsiUp !== 'undefined') {
resolve();
} else {
reject(new Error('AnsiUp did not define global AnsiUp object after script load.'));
}
};
script.onerror = (event) => {
// event might be an ErrorEvent or just a generic Event.
// For more detail, one might check event.message or event.type.
console.error("Error loading AnsiUp script:", event);
reject(new Error('Failed to load AnsiUp script from ' + ansiUpUrl));
};
});
document.head.appendChild(script);
try {
await loadPromise;
} catch (e) {
console.error("AnsiUp SCRIPT loading error:", e.message);
const pre = document.createElement('pre');
pre.textContent = "Error: Could not load AnsiUp library. " + e.message;
return pre;
}
} else {
// Script tag exists, but AnsiUp might not be defined yet (e.g. still loading)
// Poll for AnsiUp global variable to become available.
try {
await new Promise((resolve, reject) => {
let attempts = 0;
const intervalId = setInterval(() => {
if (typeof AnsiUp !== 'undefined') {
clearInterval(intervalId);
resolve();
} else if (attempts++ > 100) { // Timeout after ~5 seconds (100 attempts * 50ms interval)
clearInterval(intervalId);
reject(new Error('AnsiUp not defined even after script tag was found (timeout).'));
}
}, 50); // Check every 50ms
});
} catch (e) {
console.error("AnsiUp POLLING error:", e.message);
const pre = document.createElement('pre');
pre.textContent = "Error: AnsiUp library not available. " + e.message;
return pre;
}
}
}
// Final check: If AnsiUp is still not available, something went wrong.
if (typeof AnsiUp === 'undefined') {
console.error("Critical: AnsiUp is still not defined after loading attempts.");
const pre = document.createElement('pre');
pre.textContent = "Error: AnsiUp library failed to initialize correctly.";
return pre;
}
const ansi_up = new AnsiUp();
// Prepare canvas for image processing
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Optimization for getImageData
// Ensure the image is loaded and valid
if (!originalImg || !originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
console.error("Image is not fully loaded, is invalid, or was not provided.");
const pre = document.createElement('pre');
pre.textContent = "Error: Input image is not loaded or is invalid.";
pre.style.color = "red";
return pre;
}
const imgAspectRatio = originalImg.height / originalImg.width;
// This factor adjusts for non-square character cells in typical terminal fonts (e.g., 8x16px).
// It means characters are often twice as tall as they are wide.
// To make the image appear with correct proportions in such a font,
// we sample fewer vertical pixels per character row relative to horizontal.
// A value of 0.5 assumes font cell height is twice its width.
// For square cells (e.g. if using a 'perfect' block font or specific CSS), this should be 1.0.
const charCellAspectRatioCorrection = 0.5;
const targetHeightChars = Math.max(1, Math.round(targetWidth * imgAspectRatio * charCellAspectRatioCorrection));
// Set canvas dimensions for sampling: one "pixel" on this canvas per output character.
canvas.width = targetWidth;
canvas.height = targetHeightChars;
// Draw the image onto the canvas, scaled to the target character dimensions.
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
// This can happen if the image is cross-origin and the canvas becomes "tainted".
console.error("Error getting ImageData (possibly from a tainted canvas):", e);
const pre = document.createElement('pre');
pre.textContent = "Error: Could not get image data. If using a cross-origin image, ensure CORS is enabled for it.";
pre.style.color = "red";
return pre;
}
const data = imageData.data;
let ansiString = "";
const ansiResetCode = "\x1b[0m"; // ANSI Reset All Attributes code
// Iterate over the sampled "pixels" (which correspond to character cells)
for (let y = 0; y < canvas.height; y++) { // y is character row
let currentLineAnsi = "";
for (let x = 0; x < canvas.width; x++) { // x is character column
const pixelIndex = (y * canvas.width + x) * 4;
const r = data[pixelIndex];
const g = data[pixelIndex + 1];
const b = data[pixelIndex + 2];
const a = data[pixelIndex + 3];
if (a < 128) { // If pixel is significantly transparent, use a space
// This relies on the <pre> element's background color.
// For more control, one could set an ANSI background color here.
currentLineAnsi += " ";
} else {
const ansiColorCode = findClosestAnsiColor(r, g, b, modernAnsiPalette);
currentLineAnsi += ansiColorCode + character;
}
}
// Add reset code at the end of each line to contain color effects to that line.
ansiString += currentLineAnsi + ansiResetCode + "\n";
}
// Convert the raw ANSI string to HTML using AnsiUp
const htmlOutput = ansi_up.ansi_to_html(ansiString);
// Create a <pre> element to display the HTML content
const preElement = document.createElement('pre');
preElement.innerHTML = htmlOutput; // Use innerHTML as ansi_to_html returns an HTML string
// Style the <pre> element for a terminal-like appearance
preElement.style.fontFamily = "monospace, 'Courier New', Courier"; // Monospaced font is crucial
preElement.style.fontSize = "10px"; // Adjust for desired density/size
preElement.style.lineHeight = "1.0"; // Attempt to make character blocks touch vertically
preElement.style.backgroundColor = "black"; // Default terminal background color
preElement.style.color = "white"; // Default text color (if not overridden by ANSI)
preElement.style.whiteSpace = "pre"; // Preserve whitespace and line breaks
preElement.style.overflow = "auto"; // Add scrollbars if content is too large
preElement.style.margin = "0"; // Remove default margins from <pre>
return preElement;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image To ANSI Terminal Art Converter transforms images into ANSI art suitable for terminal displays. Users can specify the target width of the output and choose a character to represent each pixel. This tool is particularly useful for creating retro-style artwork, programming projects, and displaying images in environments where graphical interfaces are not available. It can be employed in coding communities, for customizing terminal outputs, or for creating unique visual elements in text-based applications.