Img2Num UMD Example Application

A simple demo app showing how to use Img2Num's JavaScript library as an AMD module. RequireJS loads the UMD build from lib/, so nothing is added to the global scope.

Upload an image to convert it to an SVG.

Original

Result

How this page works

Load the library from a CDN (UMD 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 src="https://cdn.jsdelivr.net/npm/[email protected]/require.js"></script>
<script>
  requirejs.config({ paths: { img2num: "https://cdn.jsdelivr.net/npm/[email protected]/dist/standalone/img2num.umd" } });
  requirejs(["img2num"], ({ imageToUint8ClampedArray, imageToSvg }) => {
    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.