Img2Num html-js (ESM) Example Application

A simple demo app showing how to use Img2Num's JavaScript library as a native ES module via <script type="module">, straight from lib/ with no bundler.

Upload an image to convert it to an SVG.

Original

Result

How this page works

Load the library from a CDN (ESM format) — this page ships its own copy in lib/ so it also works offline:

<label id="dropZone" class="dropzone" for="fileInput" tabindex="0">
  <span id="uploadHelp">Drag &amp; drop an image here, paste one (Ctrl+V), or&nbsp;</span>
  <input
    type="file"
    id="fileInput"
    aria-describedby="uploadHelp"
    accept=".png,.jpg,.jpeg,.gif,.bmp,.webp,.avif,image/png,image/jpeg,image/gif,image/bmp,image/webp,image/avif"
  />
</label>
<img id="originalImg" alt="" />
<div id="spinner" class="spinner"></div>
<img id="previewImg" alt="" />

<script type="module">
  import { imageToUint8ClampedArray, imageToSvg } from "https://cdn.jsdelivr.net/npm/[email protected]/dist/browser/img2num.js";

  const fileInput = document.getElementById("fileInput");
  const dropZone = document.getElementById("dropZone");
  const originalImg = document.getElementById("originalImg");
  const previewImg = document.getElementById("previewImg");
  const spinner = document.getElementById("spinner");
  
  let latestRequest = 0;
  
  async function processImage(file) {
    if (!file || !file.type.startsWith("image/")) return;
    const request = ++latestRequest;
  
    if (originalImg.src.startsWith("blob:")) {
      URL.revokeObjectURL(originalImg.src);
    }
    originalImg.src = URL.createObjectURL(file);
    previewImg.removeAttribute("src");
    spinner.style.display = "block";
  
    try {
      const { pixels, width, height } = await imageToUint8ClampedArray(file);
      const { svg } = await imageToSvg({ pixels, width, height });
      if (request !== latestRequest) return;
      previewImg.src = "data:image/svg+xml;base64," + btoa(svg);
    } catch (error) {
      console.error("Failed to convert image:", error);
      if (request === latestRequest) {
        alert("An error occurred.\nCheck the developer console for details.");
      }
    } finally {
      if (request === latestRequest) spinner.style.display = "none";
      // Note: terminateWasmModule() is deliberately NOT called here. It frees
      // the WASM resources, which would force a full re-initialization on the
      // next conversion. Cleanup happens automatically on page teardown.
    }
  }
  
  // File picker
  fileInput.addEventListener("change", (e) => processImage(e.target.files[0]));
  
  // Paste (Ctrl+V)
  document.addEventListener("paste", (e) => {
    const item = [...e.clipboardData.items].find((i) => i.type.startsWith("image/"));
    if (item) processImage(item.getAsFile());
  });
  
  // Drag & drop
  dropZone.addEventListener("dragover", (e) => {
    e.preventDefault();
    dropZone.classList.add("dropzone--active");
  });
  
  dropZone.addEventListener("dragleave", () => {
    dropZone.classList.remove("dropzone--active");
  });
  
  dropZone.addEventListener("drop", (e) => {
    e.preventDefault();
    dropZone.classList.remove("dropzone--active");
    const file = [...e.dataTransfer.files].find((f) => f.type.startsWith("image/"));
    if (file) processImage(file);
  });
  
  // Keyboard (Enter/Space on the focused drop zone)
  dropZone.addEventListener("keydown", (e) => {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      fileInput.click();
    }
  });
</script>

Or install it locally: npm install img2num — see the installation guide.

Full page source is viewable with your browser's View Source — it's a complete, standalone app. See the usage guide for options like num_colors and the bilateral filter parameters.