Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Puppeteer to render HTML and create the PDF; use Socket.IO only to submit jobs and report their state. The essential call is await page.pdf(options). It resolves to a Uint8Array, which you can write to a file or keep in memory. Socket.IO then carries events such as pdf:request, pdf:progress, pdf:complete and pdf:error between your client and server.

This separation keeps rendering logic testable and lets you choose a delivery method. The example below emits progress and a job result, while an ordinary HTTP endpoint serves the generated file. That download route is an application design choice; Socket.IO itself is the coordination layer.

What each component does

  • Node.js runs the application and owns the job lifecycle.
  • Puppeteer launches Chromium, loads the HTML, applies print settings and calls page.pdf().
  • Socket.IO provides bidirectional, event-based communication. It can use WebSocket and fall back to HTTP long-polling, and it can reconnect after a dropped connection.

As the Socket.IO maintainer describes it, the library enables “low-latency, bidirectional and event-based communication between a client and a server.” It does not render HTML and it does not create PDFs.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prerequisites and project setup

Use a current Node.js LTS release, a package manager, and a Chromium-compatible environment. Puppeteer normally downloads a browser during installation. In a restricted deployment, install and configure a compatible browser separately and set Puppeteer’s executable path according to that environment.

#1 Best Overall
Sale
Epson Workforce ES-50 Compact & Lightweight Mobile Document Scanner
  • PORTABLE SCANNER FOR USE ON-THE-GO — The fastest and lightest mobile single-sheet-fed compact document scanner in its class¹
  • QUICK DOCUMENT SCANNING ― This Epson ultra-fast scanner scans a single page as quickly as 5.5 seconds²; Windows and Mac compatible
  • VERSATILE PAPER HANDLING ― Portable scanner scans documents up to 8.5 x 72 in; Also easily digitizes receipts and ID cards to make accounting, bookkeeping, and organizing simpler
  • INTUITIVE, HIGH-SPEED SOFTWARE — Epson ScanSmart Software³ is a smart tool allowing you to easily scan, review, and save; Stay organized easily with the help of this Epson scanner
  • EASY SETUP — USB-powered connect to your computer for quick and simple scanning; No batteries or external power supply required to operate portable document scanner; Standard Connectivity: USB 2.0
  1. Create a project: mkdir pdf-socket-server && cd pdf-socket-server.
  2. Initialize it: npm init -y.
  3. Install dependencies: npm install express socket.io puppeteer.
  4. Add an HTML file or URL that Chromium can reach from the server.

The code uses ECMAScript modules. Add "type": "module" to package.json, then save the server as server.js.

Complete Node.js server

This server accepts a URL, creates a Puppeteer page, emits progress, and stores the returned bytes so an HTTP route can download them. In production, authenticate clients, validate allowed URLs and impose your own queue, timeout and storage policies.

import express from 'express';
import http from 'node:http';
import crypto from 'node:crypto';
import { Server } from 'socket.io';
import puppeteer from 'puppeteer';

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  cors: { origin: true, credentials: true }
});

const jobs = new Map();
let browserPromise;

function getBrowser() {
  if (!browserPromise) browserPromise = puppeteer.launch();
  return browserPromise;
}

app.get('/pdf/:jobId', (req, res) => {
  const job = jobs.get(req.params.jobId);
  if (!job || !job.bytes) return res.status(404).send('PDF not found');
  res.type('application/pdf');
  res.set('Content-Disposition', `attachment; filename="${job.jobId}.pdf"`);
  res.send(Buffer.from(job.bytes));
});

io.on('connection', (socket) => {
  socket.on('pdf:request', async (input, reply) => {
    const jobId = crypto.randomUUID();
    const url = typeof input?.url === 'string' ? input.url : '';
    if (!/^https?:///i.test(url)) {
      const error = { message: 'A valid http or https URL is required' };
      socket.emit('pdf:error', { jobId, ...error });
      if (reply) reply({ ok: false, jobId, ...error });
      return;
    }

    jobs.set(jobId, { jobId, status: 'queued', bytes: null });
    socket.emit('pdf:progress', { jobId, status: 'queued' });
    if (reply) reply({ ok: true, jobId });

    let page;
    try {
      const browser = await getBrowser();
      page = await browser.newPage();
      jobs.get(jobId).status = 'loading';
      socket.emit('pdf:progress', { jobId, status: 'loading' });

      await page.goto(url, {
        waitUntil: 'networkidle2',
        timeout: 60000
      });

      jobs.get(jobId).status = 'rendering';
      socket.emit('pdf:progress', { jobId, status: 'rendering' });

      const bytes = await page.pdf({
        format: 'A4',
        printBackground: true,
        margin: { top: '16mm', right: '16mm', bottom: '16mm', left: '16mm' }
      });

      jobs.get(jobId).bytes = bytes;
      jobs.get(jobId).status = 'complete';
      socket.emit('pdf:complete', {
        jobId,
        downloadUrl: `/pdf/${jobId}`
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      const job = jobs.get(jobId);
      if (job) job.status = 'failed';
      socket.emit('pdf:error', { jobId, message });
    } finally {
      if (page) await page.close().catch(() => {});
    }
  });
});

async function shutdown() {
  if (browserPromise) {
    const browser = await browserPromise.catch(() => null);
    if (browser) await browser.close();
  }
  server.close(() => process.exit(0));
}
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

server.listen(3000, () => console.log('Listening on http://localhost:3000'));

Start it with node server.js. The browser is reused between jobs, while each job receives a fresh page. The page is closed in finally, including failure paths. Add an expiry policy to jobs in a real service so completed PDF bytes do not remain in memory indefinitely.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Socket.IO client and event flow

Install the browser client with npm install socket.io-client, or load the client bundle in a web application. A minimal client is:

import { io } from 'socket.io-client';

const socket = io('http://localhost:3000');

socket.on('connect', () => {
  socket.emit('pdf:request', { url: 'https://example.com' }, (ack) => {
    if (!ack.ok) console.error(ack.message);
    else console.log('Job accepted:', ack.jobId);
  });
});

socket.on('pdf:progress', (event) => {
  console.log(event.jobId, event.status);
});

socket.on('pdf:complete', (event) => {
  console.log('Download:', `http://localhost:3000${event.downloadUrl}`);
});

socket.on('pdf:error', (event) => {
  console.error('PDF failed:', event.jobId, event.message);
});

The acknowledgement confirms acceptance; progress events describe the server’s stages; completion supplies the application’s download URL. A reconnecting client should query job status or use an idempotency key if it must recover jobs after losing a connection. Do not assume that reconnecting automatically replays every event.

Rank #2
Sale
Brother DS-640 Compact Mobile Document Scanner, (Model: DS640)
  • FAST SPEEDS - Scans color and black and white documents a blazing speed up to 16ppm (1). Color scanning won’t slow you down as the color scan speed is the same as the black and white scan speed.
  • ULTRA COMPACT – At less than 1 foot in length and only about 1. 5lbs in weight you can fit this device virtually anywhere (a bag, a purse, even a pocket).
  • READY WHENEVER YOU ARE – The DS-640 mobile scanner is powered via an included micro USB 3. 0 cable allowing you to use it even where there is no outlet available. Plug it into you PC or laptop and you are ready to scan.
  • WORKS YOUR WAY – Use the Brother free iPrint&Scan desktop app for scanning to multiple “Scan-to” destinations like PC, Network, cloud services, Email and OCR. (2) Supports Windows, Mac and Linux and TWAIN/WIA for PC/ICA for Mac/SANE drivers. (3)
  • OPTIMIZE IMAGES AND TEXT – Automatic color detection/adjustment, image rotation (PC only), bleed through prevention/background removal, text enhancement, color drop to enhance scans. Software suite includes document management and OCR software. (4)

How Puppeteer renders and creates the PDF

Navigate deliberately

page.goto() loads the document. Puppeteer’s guide demonstrates waitUntil: 'networkidle2', which waits for a low number of active network connections. It is an example, not a universal rule: applications with polling, analytics or long-lived connections may never reach the state you expect. For a known page, waiting for a selector or a short, explicit application-ready signal can be more reliable.

Call page.pdf()

page.pdf() generates the PDF and returns a promise for a Uint8Array. Fonts are awaited by default according to Puppeteer’s PDF guide. With path omitted, Puppeteer does not write a file; the bytes remain available to your code. Supplying path tells Puppeteer where to write the output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const bytes = await page.pdf({
  path: './output/invoice.pdf',
  format: 'Letter',
  printBackground: true
});

Use either destination deliberately. A path is convenient for a local artifact or a subsequent upload. In-memory bytes avoid an intermediate file but increase per-job memory pressure; the available material does not establish a universal size or concurrency limit.

Print media versus screen media

PDF generation uses print CSS by default. To render the screen stylesheet instead, call:

await page.emulateMediaType('screen');
const bytes = await page.pdf({ printBackground: true });

Printing also modifies colors by default. If exact colors matter, use CSS such as -webkit-print-color-adjust: exact on the relevant elements and verify the result in your target Chromium version.

Rank #3
Canon imageFORMULA R10 - Portable Document Scanner, USB Powered, Duplex Scanning, Document Feeder, Easy Setup, Convenient, Perfect for Mobile Users, White
  • STAY ORGANIZED – Easily convert your paper documents into digital formats like searchable PDF files, JPEGs, and more.Power Consumption : 2.5W or less (Energy Saving Mode: 0.7W). Suggested Daily Volume : 500 scans..Does it contain liquid: no
  • CONVENIENT AND PORTABLE –lightweight and small in size, you can take the scanner anywhere from home offices, classrooms, remote offices, and anywhere in between
  • HANDLES VARIOUS MEDIA TYPES – Digitize receipts, business cards, plastic or embossed cards, reports, legal documents, and more
  • FAST AND EFFICIENT – No technical hurdles or complicated setups here; easily scan both sides of a document at the same time, in color or black-and-white, at up to 12 pages-per-minute, and with a 20 sheet automatic feeder
  • BROAD COMPATIBILITY – Works with both Windows and Mac devices, be it laptop or computer

Paper, dimensions and headers

The PDF options support named formats and explicit dimensions. Letter is the documented default, and format takes priority over width and height. Do not set contradictory values and expect width or height to win. Header and footer templates are available through the PDF options; enable display of header and footer when you need them, and reserve sufficient margins so content is not covered.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing an output and interaction pattern

Decision Option Use it when
Output path You need a filesystem artifact or will upload it after generation.
Output Returned Uint8Array You will stream, store or process bytes without an intermediate file.
Media Print (default) Your document has print-specific CSS.
Media Screen via emulateMediaType('screen') The PDF should match the on-screen stylesheet.
Interaction Socket.IO job events Generation is asynchronous and the UI needs progress or completion notifications.
Interaction HTTP request/download A conventional request/response is sufficient for your application.

Socket.IO does not, by itself, define a browser download attachment experience or a version-specific maximum PDF payload. The example therefore uses events for coordination and an explicitly implemented HTTP route for retrieval.

Reliability, security and performance considerations

  • Limit navigation: accept only permitted schemes and, where appropriate, an allow-list of hosts. This reduces server-side request-forgery risk.
  • Set timeouts: navigation and job deadlines prevent a page that never becomes idle from occupying a worker forever.
  • Control concurrency: use a queue and a bounded number of Chromium pages. No universal safe number is established; measure your own workload.
  • Clean up: close every page, close the browser during shutdown, and expire stored jobs and bytes.
  • Protect events and downloads: authenticate Socket.IO connections and authorize access to each job ID. Do not expose predictable or unauthenticated PDF URLs.
  • Make retries safe: assign a client request ID and decide whether a retry creates a new job or returns an existing one.
  • Observe stages: log job ID, URL host, duration, status and failure reason without logging secrets embedded in URLs.

Troubleshooting

Chromium fails to launch

Check that Puppeteer’s browser was installed and that the deployment has the libraries and sandbox permissions Chromium requires. If you supply a custom executable, verify its path and compatibility rather than changing application code blindly.

The job stays in loading

The selected waitUntil condition may not fit the page. Replace it with a readiness selector or application signal, and keep a finite timeout. Pages that maintain WebSocket or polling connections are poor candidates for a network-idle-only rule.

Fonts or images are missing

Wait for the page’s own readiness condition, ensure assets are reachable from the server, and inspect browser logs and response status codes. Puppeteer awaits fonts during PDF generation, but unavailable font files still cannot render.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
IRIScan Express 4 Black Compact Portable USB Simplex Document Scanner, 8 PPM for Contracts, Invoices and Business Cards, Compatible with Windows, Readiris PDF Included
  • IRIScan Express, portable scanner : scans color and black and white documents a blazing speed up to 8ppm simplex. Color scanning won’t slow you down as the color scan speed is the same as the black and white scan speed.
  • IRIScan Express mobile scanner is powered via an included micro USB 2. 0 cable allowing you to use it even where there is no outlet available. Plug it into you PC or laptop and you are ready to scan. USB cable provided. AC Adapter not provided and not needed.
  • IRIScan flatbed scanner uses a simplex scanning mode allows for quick and straightforward scanning of single-sided documents. IRIScan with its full portable features is the ideal document scanners for computers.
  • IRIScan document scanner : Versatile scanning capabilities, including scanning to Word, PDF, and Excel formats with companion software provided Readiris OCR
  • Receipt scanner and card scanner with Additional features include scanning business cards directly to Outlook, photo scanning, and receipt scanning for efficient document management

Colors or backgrounds differ

Remember that print media and print color adjustment are defaults. Use emulateMediaType('screen') when appropriate, set printBackground: true, and apply -webkit-print-color-adjust: exact where exact color reproduction is required.

The PDF is cut off or the wrong size

Check the selected paper format, margins, CSS page rules and whether format overrides width and height. Long unbroken elements may also force unexpected page breaks.

The client misses completion

A dropped connection can occur before an event arrives. Persist job state server-side, let clients request status after reconnecting, and make completion retrieval idempotent. Socket.IO’s reconnection support does not remove the need for application-level job recovery.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you only need a clean screenshot or PDF of a URL, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes 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. Its MCP tools—take_screenshot, get_page_info and capture_pdf—let Claude, Cursor and other MCP clients request captures.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For PDF output, use the API endpoint and options documented at https://screenshotneo.com/docs/. A basic request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The same endpoint can return PNG, JPEG, WebP or PDF according to the documented parameters. It also supports full-page capture, element selectors, dark mode, device presets, retina scale, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting and an OpenAPI specification. Parameter names used by other screenshot APIs are accepted to ease migration.

Best Value
Canon Canoscan Lide 300 Scanner (PDF, AUTOSCAN, Copy, Send)
  • Scanner type: Document
  • Connectivity technology: USB
  • With Auto Scan Mode, the scanner automatically detects what you're scanning
  • Digitize documents and images

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 shots each month without a card; paid plans start at $5 for 3,000 shots. Sign up for ScreenshotNeo.

Frequently Asked Questions

Can I generate a PDF without Socket.IO?

Yes. Puppeteer can be called from a normal HTTP request, queue worker or command-line process. Socket.IO is useful when a client needs bidirectional job events, but it is not required for rendering.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does page.pdf() return a Buffer?

The documented return value is a promise resolving to a Uint8Array. Convert it with Buffer.from(bytes) when an Express response or Node filesystem API needs a Buffer.

Should every page use networkidle2?

No. Puppeteer’s guide uses it as an example. Choose a readiness condition that matches the page and enforce a finite timeout.

Can Socket.IO carry the PDF itself?

The available documentation establishes event communication but not a version-specific large-binary limit or a canonical PDF-transfer pattern. Treat binary delivery as a separately documented design decision; an HTTP download route is one conventional option.

Quick Recap

Bestseller No. 3
Canon imageFORMULA R10 - Portable Document Scanner, USB Powered, Duplex Scanning, Document Feeder, Easy Setup, Convenient, Perfect for Mobile Users, White
Canon imageFORMULA R10 - Portable Document Scanner, USB Powered, Duplex Scanning, Document Feeder, Easy Setup, Convenient, Perfect for Mobile Users, White
BROAD COMPATIBILITY – Works with both Windows and Mac devices, be it laptop or computer; This product is not intended for scanning photographs on photo paper / photographic media
$184.00
Bestseller No. 4
IRIScan Express 4 Black Compact Portable USB Simplex Document Scanner, 8 PPM for Contracts, Invoices and Business Cards, Compatible with Windows, Readiris PDF Included
IRIScan Express 4 Black Compact Portable USB Simplex Document Scanner, 8 PPM for Contracts, Invoices and Business Cards, Compatible with Windows, Readiris PDF Included
Find our Software here : irislink.com/start; IRIScan Express is only compatible Windows platform and not macintosh
$129.00
Bestseller No. 5
Canon Canoscan Lide 300 Scanner (PDF, AUTOSCAN, Copy, Send)
Canon Canoscan Lide 300 Scanner (PDF, AUTOSCAN, Copy, Send)
Scanner type: Document; Connectivity technology: USB; With Auto Scan Mode, the scanner automatically detects what you're scanning
$75.00

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.