Quick start
1. Put the agent on the computer that has the printer
Copy the binary for that OS from bin/:
| OS | File |
|---|---|
| Windows 10/11 x64 | thermalink-windows-amd64.exe (rename to thermalink.exe if you like) |
| macOS Apple Silicon | thermalink-darwin-arm64 |
| macOS Intel | thermalink-darwin-amd64 |
| Linux x64 / ARM64 (incl. Raspberry Pi 4/5 64-bit) | thermalink-linux-amd64 / thermalink-linux-arm64 |
macOS/Linux: chmod +x thermalink-*. On macOS, the first run may be blocked by Gatekeeper. Right-click the file → Open, or run xattr -d com.apple.quarantine thermalink-darwin-arm64.
Windows SmartScreen may warn about an unrecognised app. Choose More info → Run anyway. (The binaries are not code-signed. The full source is in src/ if you prefer to build it yourself.)
2. Find and name your printer
thermalink printers
Measure its real print width (printers differ: 203 vs 180 dpi, 80 vs 58 mm paper, and the printer's own paper-width setting):
thermalink calibrate "EPSON TM-T20III Receipt"
The page shows black bars of 384 / 432 / 512 / 576 / 640 dots, each with its number at its right end. The widest bar whose number is fully readable is your width (wider bars get clipped by the printer, cutting off the number). A dotted ruler shows how many characters fit per line in text mode.
Then name the printer with an alias and its width, so your app never hard-codes OS printer names or sizes:
thermalink alias receipt "EPSON TM-T20III Receipt" --dots 576 # USB printer installed in the OS
thermalink alias kitchen tcp://192.168.1.50:9100 --dots 512 # network printer
thermalink alias slip "POS-58" --dots 384 --mode text # 58 mm printer using its built-in font
thermalink alias labels "4BARCODE 3B-365B" # label printer (send raw TSPL/ZPL)
thermalink default receipt
Nominal widths: 80 mm paper @203 dpi = 576, 80 mm @180 dpi = 512, 58 mm @203 dpi = 384. Real printers often lose a few dots to their own margins. For example, a BIXOLON SRP-352plusIII (nominally 576) measured 568 usable dots, so always trust the ruler over the spec sheet. Image mode also keeps 8 blank dots on each side by default (--margin).
USB printers must be installed in the OS first, the normal way: the vendor driver on Windows, or CUPS on macOS/Linux. Thermalink sends bytes as RAW through that queue, so the driver doesn't re-render them.
3. Allow your web app
thermalink allow https://pos.yourapp.com
thermalink allow http://localhost:3000 # for development
Only these origins can print. Any other website gets a 403.
4. Print a test page
thermalink test
5. Run it permanently
thermalink install # starts at login (Windows Task Scheduler / macOS LaunchAgent / systemd --user)
thermalink uninstall
Or just run thermalink (no arguments) in a terminal while you develop.
6. Print from your app
<script src="thermalink.js"></script>
<script>
const tl = new Thermalink();
async function printReceipt(order) {
const status = await tl.status();
if (status === 'permission_prompt') return alert('Click "Allow" in the browser prompt next to the address bar, then print again');
if (status === 'permission_denied') return alert('The browser is blocking the printer helper. Allow "Apps on device" in this site's settings and reload');
if (status !== 'running') return alert('Printer helper is not running on this computer');
const r = Thermalink.receipt().title('MY SHOP');
order.lines.forEach(l => r.row(`${l.qty} x ${l.name}`, l.total.toFixed(2)));
r.line().row('TOTAL', order.total.toFixed(2), { bold: true }).cut();
await tl.print(r, { printer: 'receipt' });
}
</script>
Or with a bundler: copy sdk/thermalink.js + sdk/thermalink.d.ts into your project and import Thermalink from './thermalink.js'.
Open examples/playground.html from a local web server (e.g. npx serve . then thermalink allow http://localhost:3000) to design receipts with a live preview.
Paper width
Set it per printer with --dots (see step 2). Printers without a profile use the global paperDots (default 576) and paperWidth (default 48) from the config file, and a receipt can override both with dots/width.
More docs: Reference · Troubleshooting · Compatibility