<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image to ASCII Art Converter</title>
</head>
<body>
  <div>
    <textarea id="charsetInput" placeholder="Enter characters to use for ASCII art"></textarea>
  </div>
  <div>
    <input type="file" id="imageInput">
  </div>

    <pre id="asciiOutput"></pre>
    <script src="script.js"></script>
</body>
</html>
html, body {
    height: 100%;
    margin: 0;
    font-family: 'Courier New', Courier, monospace; /* This is just a common choice for ASCII art, but you can choose any monospaced font. */
    text-align: center;
}

pre {
    margin: 0;
  font-size: 6px;
}

textarea {
  margin-top: 16px;
  margin-bottom: 8px;
  width: 200px;
  height: 120px;
}
let currentImage = null;

document.getElementById('imageInput').addEventListener('change', function() {
  const file = this.files[0];
  if (file) {
    currentImage = file;
    convertImageToASCII(file);
    document.getElementById('imageInput').classList.add('hidden');  // Hide the input using CSS class
  }
});

document.getElementById('charsetInput').addEventListener('input', function() {
  if (currentImage) {
    convertImageToASCII(currentImage);
  }
});

function getUniqueCharactersFromString(str) {
  return Array.from(new Set(str.split(''))).join('');
}

function getSortedCharactersByBrightness(characters) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const brightnessValues = [];

  canvas.width = 100;
  canvas.height = 100;

  for (let i = 0; i < characters.length; i++) {
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, 100, 100);
    ctx.fillStyle = "black";
    ctx.font = "80px serif";
    ctx.fillText(characters[i], 10, 70);

    const imageData = ctx.getImageData(0, 0, 100, 100);
    let darkPixels = 0;

    for (let j = 0; j < imageData.data.length; j += 4) {
      const r = imageData.data[j];
      const g = imageData.data[j + 1];
      const b = imageData.data[j + 2];
      if (r < 128 && g < 128 && b < 128) {
        darkPixels++;
      }
    }

    brightnessValues.push({ char: characters[i], brightness: darkPixels });
  }

  brightnessValues.sort((a, b) => a.brightness - b.brightness);
  return brightnessValues.map(val => val.char).join('');
}

function convertImageToASCII(file) {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const img = new Image();
  img.src = URL.createObjectURL(file);

  img.onload = function() {
    const width = img.width;
    const height = img.height;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(img, 0, 0, width, height);

    const imageData = ctx.getImageData(0, 0, width, height).data;
    if (!document.getElementById('charsetInput').value.trim()) {
      document.getElementById('asciiOutput').innerText = '';
      return;
    }

    // Fetch unique characters from the input and sort them by brightness
    const userChars = getUniqueCharactersFromString(document.getElementById('charsetInput').value);
    const sortedChars = getSortedCharactersByBrightness(userChars);

    let asciiArt = '';
    for (let y = 0; y < height; y += 10) {
      for (let x = 0; x < width; x += 5) {
        const index = (y * width + x) * 4;
        const r = imageData[index];
        const g = imageData[index + 1];
        const b = imageData[index + 2];
        const brightness = 1 - (r + g + b) / 3 / 255;
        const charIndex = Math.floor(brightness * (sortedChars.length - 1));
        asciiArt += sortedChars[charIndex] || sortedChars[sortedChars.length - 1];
      }
      asciiArt += '\n';
    }

    document.getElementById('asciiOutput').innerText = asciiArt;
  };
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.