npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

thermal-printer-mock

v1.0.2

Published

Mock ESC/POS thermal printer with a live web preview — listens on TCP and shows every print job in the browser.

Readme

Thermal Printer Mock

A mock ESC/POS thermal printer for your laptop. Listens on a TCP port like a real network thermal printer, parses the bytes, and renders every print job live in a browser — so you can iterate on receipt layouts without owning a printer.

npm version license node

Why

Building POS / kitchen / e-commerce software that prints receipts is annoying:

  • You don't always have a thermal printer at your desk.
  • A real printer wastes paper on every layout tweak.
  • Driver bugs are easier to spot when you can see the actual bytes being sent.

This tool stands in for an Epson / Star / Bixolon-style network printer (tcp://host:9100 family). Point your application at it, and every job shows up in the UI as a rendered receipt plus the raw text, hex dump, and parsed ESC/POS tokens.

Quick start

Run instantly with npx

npx thermal-printer-mock

Then open http://localhost:4000. The mock is listening on TCP :9104.

Or clone the repo

git clone https://github.com/<your-username>/thermal-printer-mock.git
cd thermal-printer-mock
npm install
npm start

You should see:

[web]     UI ready at http://localhost:4000
[printer] mock thermal printer listening on tcp://0.0.0.0:9104

Stop with Ctrl+C.

Point your application at it

Configure your app's printer driver to use a network/TCP printer:

| Setting | Value | | ------- | ----------------- | | Host | localhost | | Port | 9104 |

Anything your app sends will appear as a new job in the UI within a few hundred ms of the connection closing or going idle.

Send a test print

A pre-built sample receipt is included:

npm run sample
# or against a custom host/port:
node scripts/sample-receipt.js 127.0.0.1 9104

Quick smoke test using nc:

printf 'Hello mock printer!\n\n\n' | nc localhost 9104

Configuration

Override defaults with environment variables:

PRINTER_PORT=9100 WEB_PORT=8080 npx thermal-printer-mock
HOST=127.0.0.1 npx thermal-printer-mock          # bind only to loopback

| Variable | Default | What it does | | -------------- | --------- | ----------------------------------------- | | PRINTER_PORT | 9104 | TCP port the mock printer listens on | | WEB_PORT | 4000 | HTTP port for the live UI | | HOST | 0.0.0.0 | Interface the printer socket binds to |

Web UI features

  • Live receipt preview — rendered at 80 mm thermal-paper width
  • Real-time updates via WebSocket as jobs arrive
  • Four views per job — Receipt · Text · Hex dump · Parsed tokens
  • Toolbar toggles — 32-column ruler, control codes, follow-latest
  • Search & time-grouped job list (Today / Yesterday / Older)
  • Stats footer — total jobs, total bytes, last activity
  • Light / dark theme (persisted)
  • Print or save as PDF the rendered receipt

Keyboard shortcuts

| Key | Action | | ----------- | ---------------------------- | | / | Focus search | | j / | Next job | | k / | Previous job | | t | Toggle theme | | ⌘K / ^K | Clear all jobs | | ⌘P / ^P | Print current receipt | | Esc | Clear search field |

Supported ESC/POS commands

Text styling, alignment, font size, double-width / double-height, underline, invert, line feed, paper cut, 1D barcodes, 2D / QR placeholder, raster & bit images (placeholder), cash drawer pulse, init, code page select, and more. Unknown commands are recorded as unknownEsc / unknownGs tokens and shown inline so you can spot what your driver sends.

The wire bytes are always preserved — open the Hex tab on any job for the exact stream.

HTTP API

| Method | Path | Description | | -------- | ---------------- | -------------------------------- | | GET | /api/history | All buffered jobs as JSON | | DELETE | /api/history | Clear the job buffer |

A WebSocket on the same port pushes init, job, and cleared events.

Troubleshooting

Port already in use. Check what's holding it:

lsof -nP -iTCP:9104 -sTCP:LISTEN

Kill it, or pick another port via PRINTER_PORT=....

Nothing shows up in the UI. The mock auto-finalizes a job ~400 ms after the last byte, or when the client closes the connection. If your driver keeps the socket open and idle without sending more data, nothing has been "completed" yet — close the connection from the client side or send a cut command.

Shadowed bind on macOS. If another process is bound to 127.0.0.1:PORT and this server is bound to *:PORT, local connections will hit the other process first. Either kill the conflicting process or change ports.

Project layout

thermal-printer-mock/
├── server.js               # TCP listener + Express/WebSocket UI server
├── lib/parser.js           # ESC/POS stream parser → tokens
├── public/                 # Static UI (HTML / CSS / JS)
└── scripts/
    └── sample-receipt.js   # Demo receipt generator

Contributing

PRs welcome — especially for ESC/POS commands not yet covered, real QR-code decoding, or driver-specific quirks. Open an issue first if it's a bigger change.

License

MIT