Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Use Mozilla PDF.js when you need an in-browser PDF preview you can control. Install the pdfjs-dist package, load a PDF through PDF.js, render pages into canvas elements, and add the controls your application needs. If you need a ready-made toolbar, start with PDF.js’s viewer layer instead of building every control yourself.
This guide covers both approaches, loading PDFs from URLs or binary data, page and zoom controls, cross-origin requirements, customization, troubleshooting, and a browser-free screenshot option.
Choose the PDF.js layer that matches your UI
PDF.js is an HTML5 PDF viewer project supported by Mozilla. Its distribution on npm is named pdfjs-dist. The project is divided into layers:
- Core: parses PDF files and performs low-level work.
- Display: exposes the JavaScript API you use to load documents, fetch pages, and render them.
- Viewer: the complete user interface with page navigation, zoom, search, thumbnails, and other controls.
Use the full viewer when you want a proven interface quickly. Use the display layer when your product needs a tailored toolbar, custom layout, application state, or a single-page preview. Mozilla asks developers embedding the viewer not to ship an unmodified copy; treat it as a starting point and adapt the UI.
Recommended Free Tools
#1 Best Overall
Install PDF.js in a JavaScript project
Create a project and install the package:
npm install pdfjs-dist
PDF.js includes a worker file. Your bundler must know where that worker is, or document loading will fail with worker-related errors. The exact import path depends on your bundler and the version you install, so expose the worker from the package according to that bundler’s asset rules.
Build a minimal custom preview
The following example uses the display layer. It loads a PDF URL, renders the first page, and provides previous, next, and zoom controls. It assumes your bundler can import the package’s modern build and worker asset.
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs';
import pdfWorker from 'pdfjs-dist/build/pdf.worker.mjs?url';
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker;
const canvas = document.querySelector('#pdf-canvas');
const context = canvas.getContext('2d');
const pageNumberLabel = document.querySelector('#page-number');
const previousButton = document.querySelector('#previous');
const nextButton = document.querySelector('#next');
const zoomInButton = document.querySelector('#zoom-in');
const zoomOutButton = document.querySelector('#zoom-out');
let pdfDocument;
let pageNumber = 1;
let scale = 1.25;
async function renderPage(number) {
const page = await pdfDocument.getPage(number);
const viewport = page.getViewport({ scale });
canvas.width = viewport.width;
canvas.height = viewport.height;
await page.render({ canvasContext: context, viewport }).promise;
pageNumberLabel.textContent = `${number} / ${pdfDocument.numPages}`;
previousButton.disabled = number <= 1;
nextButton.disabled = number >= pdfDocument.numPages;
}
async function openPdf(source) {
pdfDocument = await pdfjsLib.getDocument(source).promise;
pageNumber = 1;
await renderPage(pageNumber);
}
previousButton.addEventListener('click', async () => {
if (pageNumber > 1) await renderPage(--pageNumber);
});
nextButton.addEventListener('click', async () => {
if (pageNumber < pdfDocument.numPages) await renderPage(++pageNumber);
});
zoomInButton.addEventListener('click', async () => {
scale = Math.min(scale + 0.2, 4);
await renderPage(pageNumber);
});
zoomOutButton.addEventListener('click', async () => {
scale = Math.max(scale - 0.2, 0.5);
await renderPage(pageNumber);
});
openPdf('/documents/handbook.pdf').catch(console.error);
Pair it with simple markup:
<div class="pdf-toolbar">
<button id="previous">Previous</button>
<span id="page-number">Loading…</span>
<button id="next">Next</button>
<button id="zoom-out">−</button>
<button id="zoom-in">+</button>
</div>
<canvas id="pdf-canvas" aria-label="PDF page preview"></canvas>
For a multi-page preview, create one canvas per visible page or render pages into a virtualized list. Rendering every page immediately uses more memory; render the current page and nearby pages as the user scrolls.
Load a PDF from a URL or binary data
Loading by URL
Pass a URL string to getDocument, as in the example above. The PDF must be served in a way the browser can fetch. A URL on another origin requires the server to permit the requesting origin with appropriate CORS headers. Authentication headers, cookies, redirects, and range requests must also be allowed by that server.
Loading an ArrayBuffer or Uint8Array
If your application already downloaded the file, pass binary data instead of a URL:
Rank #2
const response = await fetch('/api/documents/42', {
credentials: 'include'
});
if (!response.ok) throw new Error(`Download failed: ${response.status}`);
const bytes = new Uint8Array(await response.arrayBuffer());
const pdf = await pdfjsLib.getDocument({ data: bytes }).promise;
await renderPageFrom(pdf, 1);
This approach lets your application control authentication and download progress. Do not expose a private document URL in the page if the browser should receive the file only after an authorization check.
Use the ready-made PDF.js viewer
The viewer application is useful when you need navigation, thumbnails, search, download, and zoom without implementing each feature. Deploy the viewer files with your application and open its viewer page with a PDF URL:
/pdfjs/web/viewer.html?file=%2Fdocuments%2Fhandbook.pdf
The file value must be URL-encoded. The viewer documentation also describes URL controls for opening at a page, selecting a zoom level, choosing a named destination, and setting sidebar mode. A typical link can therefore look like:
/pdfjs/web/viewer.html?file=%2Fdocuments%2Fhandbook.pdf#page=4&zoom=page-width
Viewer URL options are convenient for links and bookmarks, while the application API is better when your own interface needs to change the document or synchronize state. Test the options against the PDF.js version you deploy; viewer option documentation has changed over time.
Customize rendering and interaction
Responsive sizing
Compute a scale from the available container width rather than fixing one value. Obtain the page at scale 1, calculate the ratio between the container width and the viewport width, then render at that ratio. Re-render on a debounced resize event so a window drag does not start dozens of expensive paints.
High-density displays
For crisp text on a retina display, multiply the canvas backing dimensions by devicePixelRatio while keeping its CSS dimensions equal to the logical viewport. This increases memory use, so cap the ratio for very large pages or mobile devices.
Text selection and accessibility
A canvas alone is an image and does not expose selectable text. The PDF.js viewer adds text and annotation layers. If you build a custom interface, add those layers when users need search, selection, links, or accessible reading order, and keep keyboard focus visible on your controls.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Large documents
Render only pages near the viewport, cancel work for pages that have scrolled away, and remove canvases that are no longer needed. Display a loading state while getDocument resolves and handle password-protected or malformed files as recoverable errors rather than leaving a blank panel.
Cross-origin, server, and security requirements
- Serve the PDF with a correct PDF content type and a response that the browser can read.
- For a different origin, configure CORS for the exact application origin. If credentials are used, the server cannot use a wildcard origin.
- Ensure redirects preserve authorization and CORS headers.
- Do not insert untrusted PDF-derived HTML into your page. Keep PDF.js assets and worker files under your control and update them through your normal dependency process.
- Apply your own access control before issuing a document URL or returning binary data.
Same-origin restrictions are a browser rule, not a PDF.js setting. When a remote URL fails in the viewer, inspect the browser network panel and the server’s CORS response before changing rendering code.
Common errors and fixes
“Setting up fake worker” or worker loading errors
The worker URL is missing, points to a development-only path, or is blocked by your content-security policy. Import or copy the worker from pdfjs-dist, set GlobalWorkerOptions.workerSrc, and permit that script in your policy.
Rank #4
Failed to fetch or CORS errors
The browser cannot read the PDF response. Host the file on the same origin, add a CORS rule for the application origin, or fetch the file through your authenticated server and pass a Uint8Array.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →The viewer is blank but the request succeeds
Check the console for a worker-version mismatch, an invalid PDF, or a blocked worker. Confirm that the viewer and worker came from the same PDF.js package version.
Only part of a document renders
Verify that the response is not truncated and that any range-request or compression configuration is compatible with your server. Try downloading the complete file and passing binary data to separate transport problems from rendering problems.
Pages are blurry or consume too much memory
Adjust scale and device-pixel-ratio handling. Avoid rendering every page at maximum resolution; virtualize the page list and release canvases that are outside the visible region.
Performance and reliability checklist
- Lazy-render pages and cancel obsolete render tasks.
- Debounce resize and zoom operations.
- Show progress, retry transient downloads, and provide a download fallback.
- Set realistic limits for file size and page count on your server.
- Test PDFs with embedded fonts, images, annotations, encryption, very large pages, and malformed structure.
- Pin and update
pdfjs-distdeliberately; keep the viewer and worker versions aligned.
Commercial embedded alternative
PDF.js Express advertises a free in-browser viewer and a commercial Plus offering for embedding in JavaScript applications. The available material does not establish its current pricing, license terms, feature limits, or browser coverage, so verify those details directly before selecting it. For maximum control and a known open-source foundation, PDF.js display and viewer layers remain the straightforward starting point.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteBest Value
Or skip the browser setup
If your goal is a static image or PDF of a web page rather than an interactive document reader, ScreenshotNeo provides a website screenshot API. It accepts one request and returns PNG, JPEG, WebP, or PDF. Cookie and consent banners are accepted and removed before capture, along with more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
See the ScreenshotNeo documentation for options. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Frequently Asked Questions
Can I preview a PDF without showing a canvas?
Yes. Use the PDF.js viewer layer, which adds text, annotation, and navigation layers, or build those layers around the display API. A canvas by itself is only the painted page.
Does PDF.js automatically bypass CORS?
No. The PDF URL still has to satisfy browser origin rules. Configure the server or fetch the bytes through an authorized same-origin endpoint.
Should I use the viewer or display API?
Choose the viewer for a complete interface you can adapt; choose the display API when your application needs its own controls, layout, and state model.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

