DEV Community

Cover image for I Built a Free PDF to Image Converter That Runs 100% in Your Browser 🖼️⚡
BlinkNBuild
BlinkNBuild

Posted on

I Built a Free PDF to Image Converter That Runs 100% in Your Browser 🖼️⚡

Ever uploaded a PDF with math equations to ChatGPT or Gemini and watched it completely butcher the formula?

That kept happening to me with ECE study notes. The AI was hallucinating integrals and matrix expressions. Turns out most AI tools parse PDFs as text streams — not images — so anything visual (equations, diagrams, scanned pages) breaks silently.

The fix is simple: convert each PDF page to a high-res image and upload that instead. AI vision models handle it perfectly via OCR.

The problem? Every existing tool either charges you, asks you to sign up, or uploads your file to their server.

So I built the tool I needed. 100% offline. Zero uploads. Open source.

🔗 Live App → pdf-2-image.netlify.app
📦 GitHub → github.com/S-SUJAN-S/pdf-2-image


Why AI Misreads Your PDFs

When you upload a PDF to most AI tools, they extract raw text — the same way pdftotext works. This means:

  • LaTeX / math equations → garbled unicode or skipped entirely
  • Scanned textbook pages → blank (no text layer at all)
  • Circuit diagrams, annotated figures → completely invisible to the text parser

The moment you switch to image input, the AI switches to its vision model + OCR pipeline. It reads everything — equations, handwriting, diagrams — accurately.


What I Built

PDF to Image is a single-page web app. Drop a PDF. Get images. Nothing leaves your device.

Features

  • 100% Private — all processing runs inside the browser using PDF.js + HTML5 Canvas. No server, no uploads, ever.
  • 4 Quality Levels — 1× (72 DPI) to 4× Ultra HD (288 DPI)
  • PNG and JPEG output — choose based on your use case
  • Direct Folder Save — saves all pages straight to a local folder using the Web File System Access API (no 50-click download loops)
  • ZIP Download — package every page into one archive with a single click via JSZip
  • Memory-Safe Rendering — explicit canvas + worker cleanup after each page so even 200-page PDFs don't crash your tab
  • Drag & Drop — works on the whole page
  • Lightbox Gallery — click any converted page to zoom in
  • Works fully offline after first load

Tech Stack

No frameworks. No build step. No npm install.

Layer Technology
Structure Semantic HTML5
Logic Vanilla JavaScript (ES6+)
Styling Pure CSS3 with custom properties + backdrop-filter
PDF Engine Mozilla PDF.js v3.11.174
Archiving JSZip v3.10.1
Icons FontAwesome v6.4.0

The entire app is ~1,537 lines across 3 files. Open index.html and it just works.


How It Works — Core Rendering Loop

PDF.js renders each page onto a hidden <canvas>. That canvas gets exported as a PNG/JPEG Blob. That's it.

// PDF never leaves the browser — loaded from ArrayBuffer
const pdfDocument = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;

for (let pageNum = 1; pageNum <= pdfDocument.numPages; pageNum++) {
  const page = await pdfDocument.getPage(pageNum);

  // Scale controls DPI: 1.0 = 72 DPI, 4.0 = 288 DPI
  const viewport = page.getViewport({ scale: scaleValue });

  const canvas = document.createElement('canvas');
  canvas.width = viewport.width;
  canvas.height = viewport.height;

  await page.render({
    canvasContext: canvas.getContext('2d'),
    viewport: viewport
  }).promise;

  const dataUrl = canvas.toDataURL('image/png', 0.95);

  // Release GPU backing store immediately — critical for large PDFs
  canvas.width = 0;
  canvas.height = 0;
  page.cleanup();
}
Enter fullscreen mode Exit fullscreen mode

The canvas.width = 0 after each page is the important bit. Without it, rendering 100+ pages back-to-back exhausts GPU memory and crashes the tab around page 30–40.


Direct Folder Save

Instead of downloading 50 individual files, you pick a local folder and all pages save directly into it — one click, zero prompts. This uses the File System Access API:

async function saveImagesToLocalFolder() {
  const dirHandle = await window.showDirectoryPicker();

  for (const imgObj of convertedImages) {
    const fileHandle = await dirHandle.getFileHandle(imgObj.name, { create: true });
    const writable = await fileHandle.createWritable();
    await writable.write(imgObj.blob);
    await writable.close();
  }
}
Enter fullscreen mode Exit fullscreen mode

Supported on Chrome/Edge 86+. Firefox users fall back to the ZIP download automatically.


The Actual Workflow

Here's how I use it now when studying:

  1. Open pdf-2-image.netlify.app
  2. Drop the PDF (textbook chapter, scanned notes, whatever)
  3. Set quality to 2× (144 DPI) — sharp text, reasonable file size
  4. Save to folder or grab the ZIP
  5. Upload images directly to Gemini / ChatGPT

The difference in how accurately the AI reads the content is significant — especially for anything with equations, diagrams, or handwriting.


Challenges Building This

Memory management on large PDFs was the hardest part. Browsers don't garbage-collect canvas GPU memory fast enough when rendering 100+ pages sequentially. Spent a while debugging why 150-page PDFs froze the tab — the fix was aggressively calling canvas.width = 0 and page.cleanup() after every single render.

File System Access API edge cases — permission revocation mid-save, network drives, cross-origin restrictions. Required proper error handling and graceful fallback to individual Blob downloads.


Run It Locally in 30 Seconds

git clone https://github.com/S-SUJAN-S/pdf-2-image.git
cd pdf-2-image
python -m http.server 8000
# Open http://localhost:8000
Enter fullscreen mode Exit fullscreen mode

No npm. No config. Any static HTTP server works — it's just HTML, CSS, and JS.


Open Source — PRs Welcome

MIT licensed. Things I'd love help with:

  • Page range selection (e.g. convert only pages 5–12)
  • Multi-PDF batch processing
  • WEBP output format
  • Firefox File System Access API support

Fork it → github.com/S-SUJAN-S/pdf-2-image


Links


Have you run into the PDF math problem with AI tools? Or do you have a different workflow for feeding study material to AI? Drop it in the comments — would love to hear what's working for you. 👇

Top comments (0)