Replacing a Raspberry Pi with a Browser Tab

I built an auto-printer for warehouse tickets, controlled by a Raspberry Pi. It worked, but the approach had enough friction that I eventually replaced it with a browser tab.

The problem

Warehouse tickets need printing at set times each day. The manual process was: open the internal tool, select the batch, hit print. Easy to forget, and missed prints cause delays downstream.

V1: the dedicated agent

The first solution was a Node.js app on a Raspberry Pi, physically connected to the printer. It had its own document renderer, its own PDF pipeline (Puppeteer for HTML-to-PDF, CUPS for printing), and its own schedule via crontab.

It printed. But it also meant:

  • A separate piece of hardware to maintain, update, and troubleshoot when the network dropped
  • A duplicate rendering pipeline that could drift from the one in the web app
  • No admin interface. Changing the schedule meant SSH-ing into the Pi’s Linux OS and editing a crontab
  • No visibility. History was a JSON file on the Pi’s filesystem

Failures were silent - there was no alerting or batch history to check.

The insight

The warehouse operations dashboard already renders tickets and has a print button. The only thing stopping it from being automatic was the browser’s print dialog.

Chrome has a --kiosk-printing flag. When launched with this flag, window.print() sends directly to the default printer with no dialog.

V2: the browser tab

Instead of a separate system, auto-print became a feature of the existing app:

  • The app checks configured schedule times every 30 seconds
  • When a schedule fires, it fetches fresh data, renders documents using the same components as manual printing, and calls window.print()
  • Chrome’s kiosk-printing mode handles the rest

The schedule, toggle, and history all live in an admin page. No SSH, no separate hardware.

Auto-print lock

Multiple people might have the app open. Only one should print. The app uses a simple heartbeat lock: the active tab pings every 30 seconds, the lock expires after two minutes without a heartbeat. Other tabs show a standby indicator with an option to claim the lock manually.

Kiosk detection

If someone opens the app in a normal Chrome window (without --kiosk-printing), auto-print would trigger a print dialog every time a schedule fires. Not ideal.

Before printing, the app runs a quick check: trigger window.print() in a hidden iframe and time how long it takes. In kiosk mode it completes instantly. If a dialog appears and blocks, the timer exceeds the threshold and auto-print disables itself with a warning.

What changed

The Pi did one thing: print on a schedule. But it did it with a completely different print flow - its own renderer, its own PDF conversion, its own printer integration - all of which had issues. The browser-based version does the same thing by reusing the print logic that already worked.

Less hardware to maintain, and the same output comes out of the same printer.

I built a standalone test to validate the kiosk-printing approach before integrating it into the main app.