Sales and Dungeons and the pipeline that turns a web template into thermal printer commands
Sales & Dungeons — Thermal Printer as D&D / TTRPG Utility
At a glance
- What is it?
- Sales & Dungeons is a tabletop roleplaying utility that renders handouts on cheap ESC/POS thermal printers. Its interesting part is the four step pipeline, HTML template to Nunjucks render to Chrome screenshot to printer command, and the fact that the same code runs headless in a container next to a Raspberry Pi.
- Who is it for?
- Sales & Dungeons fits a game group that owns a cheap ESC/POS thermal printer and wants to print magic items, spell cards, handouts and player notes at the table without designing a layout for each one. It is the wrong tool if your printer does not speak ESC/POS, since that is the only control code set supported, and it is not a virtual tabletop, since it produces paper rather than a play surface.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 105 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A printer utility built around a cheap ESC/POS device
Sales & Dungeons is described as a thermal printer as D&D utility, and the use case it targets is specific: printing customizable handouts, quick references and other handouts for a Dungeons and Dragons or other pen and paper session.
The reasoning behind the hardware choice is in the documentation. Most thermal printers are small and can be taken with you and kept right at the gaming table, and the listed use cases range from printing magic items, spells or a letter the group found, to little character sheets for the players to use, to notes for the dungeon master.
The printer constraint is the one hard technical limit. The project supports only the ESC/POS control codes, Epson Standard Code, which the documentation calls still one of the most used control code sets. It gives a rule of thumb: most cheap Chinese thermal printers found on Amazon or AliExpress support it, most Epson thermal printers obviously support it, and a lot of older serial printers such as the Metapace T-1 also support it. The advice for anyone shopping is to check whether the printer in question mentions ESC/POS or Epson in the description or manual, and the wiki holds the list of tested printers and the settings they need.
The project is MIT licensed, its homepage is sales-and-dungeons.app, and the last push was on 2026-07-10, with tagged releases v1.3.0, v1.3.1 and v1.4.0, the last of them on the same day as the push.
One entry in the platform list deserves attention. The application runs on Windows, Mac on both Intel and M1, Linux on x64 and ARM64, and Raspberry Pi on ARMv6 and ARMv7, and then adds Android as experimental, followed by the useful line that anything else can be compiled on.
The pipeline runs HTML, Nunjucks, Chrome and ESC/POS in sequence
The How It Works section walks through the four stages, and understanding them explains most of the design decisions in the project.
Templates are created in HTML and CSS in combination with the Nunjucks templating language, and the documentation suggests imagining the templates as little websites. That is what gives you the layout options of HTML and CSS, and it also means a template can include any common framework you might need, with Fontawesome for icons given as an example.
After creating a template you create entries with the data you want to print, and Nunjucks produces the rendered HTML from that data.
Then the HTML is converted to an image, and the documentation is candid that this step is currently done by Chrome via the Chrome Debug Protocol. It calls Chrome a huge overkill for an HTML to image conversion, and explains that it is the standard solution at the moment because it supports most modern HTML and CSS features. That sentence is the honest summary of the project's largest dependency: a full browser engine in the print path.
The last step before the printer is the conversion from the rendered image to the draw image command of the printer, in the ESC/POS control codes, and that command stream is what gets sent to the device.
So the chain is template to rendered HTML to rendered image to printer command to paper. Every stage is a separate representation, and the interesting consequence is that the layout fidelity you get is exactly what Chrome can render, because Chrome is the renderer.
Data sources, importers and the community library
The application is much more than a template renderer, and the data side is where most of the work is.
There is a fast access path to external data sources such as Open5e, described as giving instant access to SRD monsters, spells and more. On top of that there are importers for data from other sources: CSV, FoundryVTT modules, the Fight Club 5e XML format, and 5eTools. For a group that already owns content in one of those formats, this is the difference between retyping a bestiary and importing it.
Templates and data sources can be imported and exported, and there is a random generator system described as extensive, which is what turns a static template into something you can roll at the table. Templates and generators are written in the same Nunjucks style, so a generator can produce entries that a template then renders.
There is also a community side: access to community templates, generators and data sources from within the app, and cloud sync for templates, generators and data sources. The cloud sync is the one part of the feature list that is not obviously local, and it is worth checking what it means for a game group that cares about its own handouts.
The AI features deserve a separate note. The app supports LLM support through OpenAI, OpenRouter and a custom local option. It can generate entries by prompt, translate entries by prompt, and execute AI prompts inside your templates and generators. Local models are supported, for example via LM Studio, or any custom endpoint that is compatible with the OpenAI API. That last point matters for a tool that prints your campaign secrets: an OpenAI compatible endpoint is the integration surface, and a local one is a supported option.
Five ways to reach the printer, and one of them needs a browser
The connection methods are listed separately from the platform list, and there are five of them: Windows Direct Printing, Raw USB Printing, CUPS for Linux and Mac, Serial, and Android USB Printing.
That list reflects a real constraint of thermal printers. A serial printer is not a USB printer, and a printer that speaks ESC/POS over a serial line needs a serial path. Direct printing on Windows uses the system's own print dialog path, and CUPS covers the Linux and Mac case where the printer is registered with the print system.
The raw USB path is where the container image comes in, and the documentation is specific about it. The headless version of Sales & Dungeons, using LibUSB, is available via a docker container, published as ghcr.io/bigjk/snd:master, and the run command is:
The container run command passes port 7123, the USB device path, the uucp group and the userdata volume, so the whole line reads docker run -p 7123:7123 --device=/dev/bus/usb --group-add uucp -v /some/place/to/persist:/app/userdata ghcr.io/bigjk/snd:master.
Each part of that line solves a specific problem. The device flag passes the USB bus through to the container, the group add grants a group that is allowed to read the device file, and the volume persists user data outside the container. The documentation spells out the substitutions: change the persist path to a folder where user data should be kept, replace the device path with the device of your own usb or serial printer, and replace uucp with a group or gid allowed to read the device file, using the stat command shown to find the right group. Once it runs, you open http://127.0.0.1:7123 in a browser.
The compose file in the documentation does the same thing declaratively:
version: "3"
services:
snd:
image: ghcr.io/bigjk/snd:master
ports:
- "7123:7123"
devices:
- "/dev/bus/usb"
group_add:
- uucp
volumes:
- "/some/place/to/persist:/app/userdata"A headless build on a Raspberry Pi with a USB thermal printer, serving the interface to a browser on the same network, is the arrangement this image is designed for.
The container image ships Chrome because the renderer needs it
The Dockerfile in the repository shows the same dependency from the other side. The build stage starts from golang:1.25-bookworm and sets the build tags with the ENV SND_TAGS=LIBUSB line, which is what switches the USB backend on at compile time. It installs Node.js from the NodeSource setup script along with libusb development headers, pkg-config and bzip2, then builds the frontend with npm ci and npm run build before running the project's own build.sh.
The release stage is the interesting one. It starts from ubuntu:latest and installs the Chrome stable package directly from Google:
RUN apt-get -y update &&\
apt-get -y install \
adduser \
libusb-1.0.0-dev \
pkg-config \
curl &&\
curl -LO https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb &&\
apt-get install -y ./google-chrome-stable_current_amd64.deb &&\That is the Chrome Debug Protocol step of the pipeline made concrete. A headless server that converts HTML to images needs a browser, so the image is large and the runtime carries a full Chrome installation, not a stripped down renderer.
The rest of the release stage is ordinary good practice. The application is copied from the build stage, a system user and group are created with a home directory, ownership of /app is handed to that user, and the container switches to it with the USER instruction, so the process does not run as root. Port 7123 is exposed and the entry point is the snd binary.
The go.mod confirms the shape of the codebase, with a module path of github.com/BigJk/snd requiring Go 1.25.0, and it lists the libraries that map onto the features: a browser automation library for the Chrome step, a USB library, a serial port library, a git library, an embedded database, a message pack format library, an HTTP framework, and a fork of an Electron library to support newer Electron versions.
Installing on a Mac means fighting the quarantine
The application is distributed through the GitHub releases page, with builds for each supported platform.
The macOS instructions exist because the mac bundles are not signed at the moment, and the documentation names the two failure messages you will hit. If macOS says the app is from an unverified developer, you can allow it through the Privacy and Security settings. If macOS reports that the app is damaged, which the documentation says can happen on M1, M2 and similar, you copy Sales & Dungeons into your Applications folder and run:
xattr -d com.apple.quarantine "/Applications/Sales & Dungeons.app/"That removes the quarantine attribute, which is what Gatekeeper uses to block an app that has not been notarised. It is a per-application exception, and it is the documented procedure for an unsigned build.
For anyone who would rather not run unsigned software, the Docker route sidesteps the problem entirely, since the container is a Linux image and the interface is served in a browser rather than installed as a desktop application.
The documentation also points at a Discord and GitHub Discussions for help, and it says plainly that if you have trouble getting this to work it is best to drop by the Discord. That is an admission that printer support is the hard part, which matches the rest of the documentation, where the tested printer list and the printer settings page are treated as first class information.
Where Sales & Dungeons is the wrong tool
The first limitation is the printer. If the device does not speak ESC/POS, the application cannot talk to it, and no amount of template work changes that. The documentation's advice is to check for ESC/POS or Epson in the manual before buying, which is worth doing before anything else in this article.
The second is the nature of the output. This produces paper. It is not a virtual tabletop, it has no map, no fog of war, no dice roller and no initiative tracker, and the project topics list it as a printer utility rather than a game engine. A group that wants a shared screen during play is looking at a different category of software, and a group that wants handouts in a chat message or a PDF is also looking elsewhere, since the pipeline ends in ESC/POS commands rather than in a document format.
The third is the dependency weight. The render path goes through Chrome, which is the right call for CSS fidelity and the wrong call for a container footprint, and the AI features add an API dependency to a tool whose core function does not need one. Neither of these is a problem for a laptop at the gaming table, and both are reasons to think twice before running the headless image on a small always-on machine.
The alternative worth naming is printing the handouts yourself. A template in any word processor or HTML to PDF pipeline gives you more layout control, no printer restriction beyond what your browser can drive, and no application to install. The difference in approach is that Sales & Dungeons is built for the moment at the table, where you want to roll a random item and have it in the printer before the players look up, with the generator, the data source and the layout in one pipeline. If your game is prep heavy and print light, the DIY route wins. If you want a magic item on paper in four seconds, that is the thing this project does.
Editorial conclusion
Sales & Dungeons fits a game group that owns a cheap ESC/POS thermal printer and wants to print magic items, spell cards, handouts and player notes at the table without designing a layout for each one. It is the wrong tool if your printer does not speak ESC/POS, since that is the only control code set supported, and it is not a virtual tabletop, since it produces paper rather than a play surface. Before you install it, confirm your printer mentions ESC/POS or Epson, read the tested printer list for its settings, and decide between the signed desktop application and the headless container at port 7123.
Frequently asked questions
What printers does Sales & Dungeons support?
Only ESC/POS, the Epson Standard Code control codes. The documentation says most cheap Chinese thermal printers and most Epson thermal printers support it, as do a lot of older serial printers such as the Metapace T-1, and the wiki lists the tested printers and their settings.
How do I run Sales & Dungeons in Docker?
Pull ghcr.io/bigjk/snd:master and run it with the USB device passed through, a group allowed to read the device, and a volume for user data, then open http://127.0.0.1:7123. The documentation gives the full docker run command and a compose example.
What does Sales & Dungeons use to render its templates?
Templates are HTML and CSS with the Nunjucks templating language, and the rendered HTML is converted to an image by Chrome via the Chrome Debug Protocol before being converted to the printer's draw image command in ESC/POS.
Can Sales & Dungeons import my existing game content?
Yes. The app imports CSV, FoundryVTT modules, the Fight Club 5e XML format and 5eTools, and it can fetch external data sources such as Open5e for SRD monsters and spells.
Does Sales & Dungeons need an AI account to work?
No, the AI features are optional. The app supports OpenAI, OpenRouter and custom local endpoints, including a local model through LM Studio or any endpoint compatible with the OpenAI API, and the printing and templating features do not depend on them.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bigjk-snd)Was this useful?