A coloring-page kiosk for kids. They browse or search a library of 240 coloring pages, tap their name, and the page comes out of your printer. Each kid gets a few pages a day, so the printer doesn't run out of paper by lunch.
| Pick who's coloring | Off to the printer |
|---|---|
![]() |
![]() |
- 240 pages, ready to go: 12 categories (animals, dinosaurs, trucks, space, unicorns, holidays...) x 10 subjects, each in an easy version (ages 3-5, chunky outlines) and a detailed one (ages 6-10).
- Safe by design: no web search and no AI at runtime. Kids only ever see the pages in the library.
- Daily allowance: each kid gets
limitpages per rolling 24 hours (3 by default). When they're out, the screen tells them when the next one unlocks. A failed print doesn't count. - Grown-up panel: hold the "Coloring Corner" title for 3 seconds and enter the PIN to give a kid an extra page, reset their count, or see recent prints.
- Two ways to print: straight to a CUPS print queue (best for a kiosk tablet), or through the browser's own Print dialog (no print server needed).
- Small and boring on purpose: one Node.js server, no npm dependencies, no database (an append-only log file).
git clone https://github.com/2shepard2/coloring-corner.git
cd coloring-corner
# 1. The pages (75 MB) live in a release download, not in git:
curl -LO https://github.com/2shepard2/coloring-corner/releases/download/v1.0.0/coloring-corner-library-v1.zip
unzip coloring-corner-library-v1.zip # creates ./library
# 2. Your kids and your settings:
cp config/kiosk.example.json config/kiosk.json # edit the names, emoji, colors, limit
cp .env.example .env # edit timezone and printer settings
# 3. A grown-up PIN (4-8 digits):
mkdir -p data secrets
shuf -i 1000-9999 -n1 > secrets/admin_pin && cat secrets/admin_pin
sudo chown -R 10240:10240 data secrets/admin_pin && sudo chmod 400 secrets/admin_pin
# 4. Go:
docker compose up -d --build
curl -s localhost:3240/healthOpen http://<your-server>:3240/ on the kids' tablet or computer.
The container runs as user 10240, which is why data/ and the PIN file are
handed to that user.
The server sends each page to a CUPS print queue, and CUPS scales it onto the paper. Kids tap "Yes, print it!" and walk over to the printer. There are no dialogs to get lost in.
- Any Linux box, Raspberry Pi or NAS running CUPS works. Add your printer there first and print a test page.
- Find the queue name with
lpstat -h your-cups-host:631 -p. - Set
CUPS_SERVER,CUPS_QUEUEandPAPER_SIZE(LetterorA4) in.env. - CUPS must accept jobs from the Coloring Corner server's address (CUPS'
"Share printers" /
Allowsettings). - Test without paper:
PRINT_DRY_RUN=1.
The page is counted against the kid's allowance, and then the browser's Print dialog opens with only the picture on the page. This is the easiest option on a laptop or desktop already set up to print. It doesn't work inside the Android kiosk app below (Android's WebView has no print dialog); use CUPS there.
config/kiosk.json:
| Key | What it does |
|---|---|
limit |
Pages per kid per rolling 24 hours |
kids |
name, emoji and color for each kid (any number of kids) |
synonyms |
Extra search words per category |
.env:
| Variable | Default | What it does |
|---|---|---|
TZ |
UTC | Your timezone, for "more at 3:15 PM" |
PORT |
3240 | Web port |
PRINT_MODE |
cups |
cups or browser |
CUPS_SERVER |
localhost:631 |
CUPS host and port |
CUPS_QUEUE |
(none) | Required for cups mode |
PAPER_SIZE |
Letter |
Letter, A4, ... |
GAMES_URL |
(none) | Shows a 🎮 Games button linking to another kids' site |
PRINT_DRY_RUN |
0 | 1 = pretend to print |
The PIN comes from the file in ADMIN_PIN_FILE (Docker) or the ADMIN_PIN
variable. With neither set, the grown-up panel is off. Five wrong tries lock
it for five minutes.
Node.js 20 or newer, plus lp (package cups-client) for CUPS mode:
LIBRARY_DIR=./library CONFIG_FILE=./config/kiosk.json ADMIN_PIN=1234 \
PRINT_MODE=browser node server.js
npm testAny tablet browser works. For a "kids can't wander off" setup, android/ has a
tiny kiosk app: a full-screen page with no address bar that can be set as the
tablet's Home app, so the Home and Back buttons stay in Coloring Corner. It
asks for the server address on first launch. Grown-ups get out through the
grown-up panel: ⚙️ Tablet settings opens Android settings (choose another
Home app there), and 🔗 Server address changes the address.
There's no prebuilt download, because an app has to be signed with a key, and
that key should be yours. Building it needs a JDK, aapt, zipalign,
apksigner, an Android API 30 android.jar and Google's
R8/D8:
keytool -genkeypair -keystore kiosk.jks -alias kiosk -keyalg RSA -validity 10000
ANDROID_JAR=/path/to/android.jar R8_JAR=/path/to/r8.jar \
KEYSTORE=kiosk.jks KEYSTORE_PASSWORD_FILE=/path/to/password.txt android/build.sh
adb install android/build/coloring.apkIt runs on Android 8 and newer, and was built for a cheap 8" Android 10 tablet. On that tablet, the page sometimes stayed blank after the app was reinstalled until the tablet was restarted.
The library was drawn by image models (about $6 for all 240 through
OpenRouter), then checked by eye, with the duds
redrawn. To add or change pages, edit scripts/subjects.json and run:
pip install pillow
OPENROUTER_API_KEY_FILE=~/openrouter.key python3 scripts/build_library.py 8 # 8 = dollar capIt only draws pages that are missing, so you can stop and rerun it, and it
stops at the dollar cap. Easy pages use black-forest-labs/flux.2-klein-4b and
detailed ones use google/gemini-3.1-flash-lite-image; override them with
EASY_MODEL / DETAILED_MODEL. Look over what it draws before kids see it,
then restart the server to load the new pages.
The pages were made with AI image models. You're free to print, share and modify them. They come as-is, like the code (MIT license).


