BIGWORDS.PAGE reference
BIGWORDS.PAGE shows a message as large as the screen allows. Everything about the display is in the URL, so sharing the link shares the display. There are no accounts and no backend, and nothing is stored anywhere.
The editor is the easiest way to make a display. All of the URL parameters are documented here, so generating links is easy too.
How the URL works
All state lives in the URL fragment, the part after #. Browsers never send the fragment to a server, so:
- the server never sees or logs your message,
- changing the fragment updates the display without reloading the page,
- one static page serves every possible display.
https://bw.teddysc.me/#Hello%20World&bg=000000&fg=ffffff&anim=pulse
After the #:
#Hello%20World&bg=000000&fg=ffffff&anim=pulse
└─ message ─┘ └──────── parameters ────────┘
The message is everything before the first unencoded &. Each segment after it is a key=value parameter.
The editor uses the same fragment at a different path:
Viewer: https://bw.teddysc.me/#Hello%20World&anim=pulse
Editor: https://bw.teddysc.me/editor#Hello%20World&anim=pulse
Pages
| Path | What you get |
|---|---|
/ (no fragment) |
Home page with examples |
/#… (any fragment) |
Viewer: fullscreen, no controls |
/editor |
Editor with live preview. Without a fragment it opens a starter message. |
/docs |
This reference |
Any non-empty fragment opens the viewer, even with no message. https://bw.teddysc.me/#&bg=ff0000 is a solid red screen.
Encoding
Use standard percent-encoding.
| Character | Write it as | Notes |
|---|---|---|
| Space | %20 |
|
| New line | %0A |
The only way to make a line break |
& |
%26 |
Required in the message, since & starts a parameter |
# |
%23 |
Required in the message |
% |
%25 |
Required for a literal percent sign |
| |
%7C |
Optional |
{ } |
%7B %7D |
Optional |
\ |
%5C |
Optional |
You can type spaces and emoji into the address bar as they are. The browser encodes them, and a link copied from the address bar has them encoded. New lines, &, # and % in the message always have to be written encoded. The editor always produces fully encoded URLs.
Messaging apps and other tools that turn URLs into links usually stop the link before punctuation at the very end, so #Hello! gets linked as #Hello. Encode punctuation at the end of a URL, such as ! as %21 or . as %2E. The editor does this for you.
Parameters
| Param | What it does | Values | Default |
|---|---|---|---|
| (message) | The text to display | Any percent-encoded text. Supports the markdown subset, {countdown}, || slide breaks and \ escapes. |
(empty) |
bg |
Background color | auto, or 6-digit hex without # (000000) |
auto |
fg |
Text color | auto, or 6-digit hex without # (ffffff) |
auto |
font |
Font | 0–6, see Fonts |
0 |
size |
Font size | auto, pixels (48), or with a unit (5vh, 8vw) |
auto |
size-min |
Smallest auto size. Text that needs to be smaller runs off the screen. | Pixels (12) or with a unit (2vh, 3vw) |
(none) |
size-max |
Largest auto size | Pixels (72) or with a unit (15vh, 10vw) |
(none) |
wrap |
Break long lines between words to fit the width, see Wrapping | on, off |
on |
pad |
Padding, in percent | 5, 5,10 or 5,10,5,10, see Padding |
5 |
anim |
Animation | none, pulse, flash, shake, bounce, scroll, crawl, typewriter, fadein, rainbow |
none |
speed |
Animation speed | slow, normal, fast |
normal |
ratio |
Fix the display to an aspect ratio | W:H (16:9, 4:3, 1:1, 9:16, 21:9…) |
(fill the screen) |
qr |
Show a QR code | A percent-encoded URL, or a Wi-Fi, phone, text, email or location code, see QR code | (none) |
qrpos |
QR code position | tl, tr, bl, br, below |
br |
qrsize |
QR code size, in percent of the display's shorter side | Whole number from 10 to 50 | 25 |
img |
Show an image | A percent-encoded public image URL | (none) |
imgpos |
Image position | bg, full, above, below |
bg |
refresh |
Reload the image every N seconds | Whole number from 1 to 86400 (larger values count as 86400) | (none) |
until |
Countdown target | ISO 8601 date and time (2026-12-31T23:59:00) |
(none) |
timer |
Countdown length, from when the page opens. Use until or timer, not both. |
Seconds (14400), or d, h, m, s amounts (4h, 1h30m) |
(none) |
cdfmt |
Countdown format | label, colon |
label |
zero |
What happens at zero | freeze, hide, or a replacement message, which can use formatting and line breaks like the main one |
freeze |
interval |
Seconds per slide | Number from 0.2 to 86400, to hundredths (2, 1.5, 0.25). Smaller values count as 0.2, larger ones as 86400. |
5 |
trans |
Slide transition | slide, none, fade, up, zoom, see Slides |
slide |
wake |
Keep the screen awake | on, off |
on |
dbg |
Show debugging information, see Debug | 0 (off), 1, 2, 3 |
0 |
An invalid value (bad hex, unknown font, impossible date…) quietly falls back to that parameter's default. The viewer never shows an error.
Formatting
Messages support a small part of Markdown. A message without any formatting shows as plain text.
| Write | Get |
|---|---|
**text** or __text__ |
Bold |
*text* or _text_ |
Italic |
***text*** or ___text___ |
Bold italic |
~~text~~ |
|
`text` |
Code: monospace on a faint box, shown exactly as typed |
# text at the start of a line |
Heading, 2× the base size |
## text at the start of a line |
Heading, 1.5× the base size |
%0A |
Line break |
Other Markdown, such as lists, links and images, shows as plain text. In a URL, write a heading's # as %23:
https://bw.teddysc.me/#%23%20Gate%2012%0ABoarding%20now
Formatting can span lines. Bold, italic and strikethrough carry over line breaks, so **WELCOME HOME,%0AGRANDMA!** is bold on both lines. A blank line or a heading ends them, and a marker without a matching one is shown as typed.
Underscores inside words are left alone, as in standard Markdown, so names like Home_Guest_5G, snake_case or first_name@example.com show exactly as typed. Only an underscore at a word boundary starts or ends emphasis.
Code is shown exactly as typed. Nothing inside backticks is treated as formatting, a slide break, {countdown} or an escape, which makes it the easy way to show passwords and codes with symbols in them. A code span has to open and close on the same line. Browsers may write a typed backtick as %60 in the address bar; both forms work.
https://bw.teddysc.me/#Price:%20~~$20~~%20**$15** ·
https://bw.teddysc.me/#Wi-Fi:%20Home_Guest_5G%0APassword:%20%60x*7|q%60
Special tokens
| Token | Meaning |
|---|---|
{countdown} |
Replaced by the live countdown when until or timer is set. Works in any slide. Shown literally if neither is set. |
|| |
Slide break |
Escapes
A backslash makes the next character literal.
| Write | Shows |
|---|---|
\|\| or \|| |
|| (not a slide break) |
\{countdown} |
{countdown} |
\*not italic\* |
*not italic* |
\# not a heading |
# not a heading |
\~\~not struck\~\~ |
~~not struck~~ |
\`not code\` |
`not code` |
\_not italic\_ |
_not italic_ |
\\ |
\ |
A backslash before any other character is dropped. In a URL you can type the backslash as-is or as %5C.
Processing order: percent-decode → escapes and code spans → split slides on || → markdown → {countdown} → restore escaped characters.
Fonts
font |
Font | License |
|---|---|---|
0 |
Your device's system font | — |
1 |
Inter | SIL OFL 1.1 |
2 |
Montserrat | SIL OFL 1.1 |
3 |
Roboto Slab | Apache 2.0 |
4 |
Bebas Neue | SIL OFL 1.1 |
5 |
JetBrains Mono | SIL OFL 1.1 |
6 |
Caveat | SIL OFL 1.1 |
All fonts ship with the site, each next to its license file. A font is only downloaded, from this site, when a display uses it. Nothing is loaded from third-party font services.
Size
With size=auto (the default), the text grows to the largest size at which the whole block fits inside the display minus its padding. Headings count at their full size. Long lines wrap between words to fit, see Wrapping. The size is recalculated whenever the window is resized or rotated, and each slide is fitted on its own.
| Value | Meaning |
|---|---|
48 |
48 pixels |
5vh |
5% of the screen height |
8vw |
8% of the screen width |
auto |
Fit to the screen (size only) |
size-maxcaps the size, however much room there is.size-minsets a floor. If the text would have to shrink below it, it stays at the minimum and runs off the edges of the screen, centered.- With neither, the size scales freely with the screen.
- Both only apply with
size=auto. A fixedsizeis used as given. 0counts as not set, sosize=0isauto.
Example: https://bw.teddysc.me/#Big%20but%20not%20huge&size-max=12vh
Wrapping
With wrap=on (the default, Word wrap in the editor), lines break between words wherever they need to, and auto-fit picks the largest size at which the wrapped text fits. Long messages come out much bigger, and the same link fills a phone held upright as well as a wide TV, since each screen wraps the text its own way. The breaks are balanced, so a wrapped line doesn't end on a single short word where the browser supports it.
- Your own line breaks and headings stay: wrapping only adds breaks.
- Lines break at spaces and after hyphens, as on any web page, and between characters in languages written without spaces, such as Chinese and Japanese. A word is never split anywhere else: one too long for the width makes the text smaller.
- With a fixed
size, text wraps at that size instead of running off the sides. crawlwraps to the width as it scrolls up.scrollmoves sideways, so it ignoreswrap.
With wrap=off, each line of the message stays on one line, so long lines make the text smaller. Add line breaks (%0A) where you want them.
Padding
pad is a percentage, written in CSS shorthand order. Top and bottom are a percentage of the display height; left and right are a percentage of its width. "Display" means the ratio region when one is set.
| Form | Example | Meaning |
|---|---|---|
| One value | pad=5 |
5% on every side |
| Two values | pad=5,10 |
5% top and bottom, 10% left and right |
| Four values | pad=5,10,5,10 |
Top, right, bottom, left |
The three-value form isn't supported. Each value must be below 50.
Colors
auto follows the viewer's light or dark setting: white on black in dark mode (and when it can't be detected), black on white in light mode. Set bg and fg independently as 6-digit hex without the #. If you set only one, the other stays auto.
https://bw.teddysc.me/#Exit&bg=0a7a2f&fg=ffffff&font=4
Animations
speed=slow|normal|fast applies to all of them. Compare
slow,
normal and
fast pulses.
Display ratio
Without ratio, the message fills the screen. With ratio=W:H it's drawn in a centered region of that shape, with the background color filling the rest. Useful for matching a projector or a video frame.
The editor also accepts a width × height in pixels and turns it into a ratio (1920 × 1080 → 16:9). Only the ratio goes in the URL.
Countdown
There are two kinds: until counts down to a date and time, and timer counts down for a length of time. Set one and put {countdown} in the message. If the message is empty, the countdown is shown on its own. Format and zero work the same for both.
Until a date and time
Set until to an ISO 8601 date and time. The time is the viewing device's local time, unless you add a zone such as Z or +02:00. The editor's date picker takes the time in your own time zone and writes it to the URL in UTC (with Z), so the countdown ends at the same moment everywhere.
For a length of time
Set timer to a length of time. The timer starts when the page opens, so reloading the page or opening the link again starts it over. Each device that opens the link runs its own timer. It keeps time while the tab is in the background or the screen is off.
timer |
Length |
|---|---|
14400 |
A plain number is seconds: 4 hours |
4h |
4 hours |
1h30m |
1 hour 30 minutes |
2d12h |
2 days 12 hours |
45s |
45 seconds |
Use any of d, h, m and s, in any order, with no spaces. Amounts add up and can go past the next unit, so 90m is 1 hour 30 minutes and 1d28h is 2 days 4 hours. The longest timer is 99 days.
https://bw.teddysc.me/#Break%20ends%20in%0A{countdown}&timer=15m&zero=Back%20to%20work!
until and timer can't be used together. If a link has both, the one that comes first is used and the other is ignored. The editor shows one at a time: pick Countdown or Timer at the top of its Countdown or timer section.
Format and zero
cdfmt |
Example |
|---|---|
label (default) |
2d 14h 06m 32s |
colon |
02:14:06:32 |
With label, units are dropped from the front once they reach zero: three hours shows as 3h 00m 00s, then 59m 59s, then 59s. Zeros after the first unit stay, so the countdown doesn't jump around as it ticks. colon always shows all four units.
zero |
One slide | Several slides |
|---|---|---|
freeze |
Stays at zero | Every {countdown} stays at zero |
hide |
The display goes blank (background only) | Slides containing {countdown} drop out; the rest keep rotating. If none remain, the display goes blank. |
| a message | The message replaces the display | The message replaces each slide that contains {countdown} |
https://bw.teddysc.me/#Doors%20open%20in%0A{countdown}&until=2027-01-01T19:00:00&zero=Welcome!
Slides
Separate slides with ||. All slides share every setting, and each is fitted on its own. A new slide comes in every interval seconds, using the transition set by trans:
trans |
The next slide… |
|---|---|
slide |
Slides in from the right |
none |
Replaces the last one at once |
fade |
Fades in as the last one fades out |
up |
Slides in from below |
zoom |
Grows into place as the last one fades out |
https://bw.teddysc.me/#9:00%20Welcome||10:30%20Workshops||12:30%20Lunch&interval=3&trans=up
Transitions last 0.6 seconds, or less when slides change quickly, so a transition always finishes well before the next one starts. When the viewing device is set to reduce motion, slide, up and zoom fade instead.
Slides changing faster than about twice a second can affect people with photosensitive epilepsy.
The editor counts the URL's characters and warns as it nears about 2,000. Some messaging apps, email clients and proxies cut off longer links.
QR code
qr puts a QR code on the display, drawn in your browser with no outside service. Its value is the text the code holds, and how it starts decides what the scanning phone does:
| Type | qr starts with |
Phone action |
|---|---|---|
| Link | Any URL, such as https:// |
Opens the link |
| Wi-Fi | WIFI: |
Offers to join the network |
| Phone call | tel: |
Offers to call the number |
| Text/SMS | SMSTO: |
Opens a new text message |
mailto: |
Opens a new email | |
| Location | geo: |
Opens a maps app (Android) |
| Plain text | Anything else | Shows the text |
The editor's QR code section has a form for each type and writes the URL for you. If you build one yourself, encode the qr value like the rest of the URL, with %26 for &.
Placement and size
| Param | What it does | Values | Default |
|---|---|---|---|
qrpos |
Where the code goes | A corner (tl, tr, bl, br), or centered below the text (below) |
br |
qrsize |
Size, in percent of the display's shorter side | Whole number from 10 to 50 |
25 |
The code scales with the screen. Larger codes scan from farther away, and a long qr value makes a denser code, which needs a larger size. The code is always black on white with a quiet zone, so it scans whatever your colors are.
Link
qr=<URL>: any URL, such as https://example.com/menu. Inside it, write & as %26, # as %23 and % as %25 (so an escaped space, %20, becomes %2520).
https://bw.teddysc.me/#Scan%20me&qr=https://bw.teddysc.me/docs&qrpos=below
Wi-Fi
qr=WIFI:T:<security>;S:<network>;P:<password>;;
Phones that scan it offer to join the network. Each field ends in ;, the fields can come in any order, and the whole value ends with one more ;.
| Field | Meaning | Values | Required |
|---|---|---|---|
T |
Security | WPA (also covers WPA2 and WPA3), WEP, or nopass for an open network |
Yes |
S |
Network name (SSID), exactly as the phone lists it | Text | Yes |
P |
Password | Text | Yes, unless T:nopass; leave it out then |
H |
Hidden network | true |
No; leave it out for a visible network |
In the network name and password, first put a \ before any \, ;, ,, : or ". Then encode the result for the URL: spaces become %20 and each \ becomes %5C. For the network Cafe Guest with the password latte;art:
| Step | Value |
|---|---|
| Fields | T:WPA; S:Cafe Guest; P:latte\;art; |
| Code text | WIFI:T:WPA;S:Cafe Guest;P:latte\;art;; |
| In the URL | qr=WIFI:T:WPA;S:Cafe%20Guest;P:latte%5C;art;; |
An open network: qr=WIFI:T:nopass;S:Library;;. A hidden one: qr=WIFI:T:WPA;S:Back%20Office;P:secret123;H:true;;.
The password is in the link and in the code, so anyone with the link or a view of the screen can read it.
Phone call
qr=tel:<number>
The number is required. Use the international form, such as +15551234567, so it works from any country; the + can be typed as is. Spaces and dashes in the number are ignored, so tel:+1-555-123-4567 works too.
Text/SMS
qr=SMSTO:<number>:<message>
| Field | Meaning | Required |
|---|---|---|
| Number | The number to text, as for Phone call | Yes |
| Message | Filled in for the sender to edit before sending. It can contain :. Leave it out along with the : before it for an empty message. |
No |
qr=mailto:<address>?subject=<subject>%26body=<message>
| Field | Meaning | Required |
|---|---|---|
| Address | Who the email goes to | Yes |
subject |
Filled-in subject | No |
body |
Filled-in message | No |
Leave out a field you don't need, and the ? too if there are none. The & between subject and body must be written %26.
Location
qr=geo:<latitude>,<longitude>
| Field | Meaning | Values | Required |
|---|---|---|---|
| Latitude | North (+) or south (−) | Decimal degrees, -90 to 90 |
Yes |
| Longitude | East (+) or west (−) | Decimal degrees, -180 to 180 |
Yes |
Android opens it in a maps app. iPhone cameras may only show it as text, so for iPhones use a link to a map instead.
https://bw.teddysc.me/#Meet%20here&qr=geo:40.6892,-74.0445&qrpos=below
Plain text
Any value that isn't one of the above, such as qr=Table%2012, goes in the code as text, decoded like the rest of the URL (Table 12). The phone shows the text and does nothing else.
Image
img takes a public image URL (http, https or data:image).
imgpos |
Behavior |
|---|---|
bg |
Fills the display, cropped as needed; text on top |
full |
Fits inside the display, letterboxed; text on top |
above / below |
Placed above or below the text, which fits into the space left |
The image loads straight from its host, which is never told which page asked for it. Like any image on the web, the host does see the viewing device's IP address and browser, and with refresh it sees a request every time the image reloads. Some hosts refuse to serve images to other sites, so an image that opens fine in its own tab can still fail here. If an image fails, the viewer leaves it out and the editor shows a warning.
refresh=N loads the image again every N seconds, for an image URL that points at something that changes, such as a webcam snapshot or a chart. Only the image reloads: the text, slides and any timer carry on. The new image replaces the old one once it has fully loaded, and if it fails to load, the last one stays up. Each reload adds a throwaway _= query parameter to the image URL, so no cache can answer it with the old image.
Tips
- Keep a slide up longer: repeat it.
A||A||Bshows A for two intervals. Withtrans=nonethe repeat is seamless. - Add a pause: leave a slide empty.
A||||Bshows a blank screen between A and B. - Keep every slide the same size: auto-fit sizes each slide on its own, so a short slide comes out bigger than a long one. Set
size-maxto about the size the longest slide gets, and they all match. - Show one word at a time: give each word its own slide, with a short
intervalandtrans=none.https://bw.teddysc.me/#**You**%7C%7C**dropped**%7C%7C**your**%7C%7C**wallet**%7C%7C**wallet**&bg=000000&fg=ffffff&font=4&size-max=50vh&interval=0.2&trans=none - Show commands and codes exactly as typed: put them in backticks, so symbols like
*and_aren't read as formatting. Without them, the stars in this command would vanish and.jpgwould turn italic.https://bw.teddysc.me/#%23%23%20Step%203%0A%60cp%20*.jpg%20*.png%20photos/%60 - Open it on another screen: in the editor, the QR button next to the URL shows the display's link as a QR code. Scan it with the tablet or phone that will show the display.
- Hide the browser: use the browser's full screen mode (F11 on Windows and Linux, Control-Command-F on a Mac), or install the app, which opens full screen where the device allows it.
Viewer behavior
- Screen wake: with
wake=on(the default) the viewer asks the browser to keep the screen on. It asks again whenever the tab becomes visible. Browsers without support ignore it. - Fullscreen: the viewer never asks for fullscreen and has no controls. Use your browser's or kiosk's own fullscreen mode.
- Tab title: always
BIGWORDS.PAGE, whatever the message. - Errors: never shown. Bad parameters use defaults; failed images are left out.
Debug
When a display doesn't look the way you expect, such as text smaller than it should be or something in the wrong place, dbg shows how the viewer laid it out. Each level adds to the one before:
dbg |
Shows |
|---|---|
0 |
Nothing. The display as normal. |
1 |
Outlines: the display (magenta), the padding (cyan), the text area (yellow), the text and each of its lines (green), the QR code (orange) and an image above or below the text (red), with a key along the top. |
2 |
Also a panel with the measurements behind the text size: whether it's running in a browser or as an installed app, the screen, window and text area sizes, the font size and whether it was fitted or fixed, the size of the text and each line, the extra room left for letters that draw past their edges, whether the font has loaded, the link exactly as the browser received it, before any decoding, and the browser's user agent. It describes the slide on screen and updates whenever the text is fitted. |
3 |
Also every size the fit tried, in order, with the size the text measured at each and whether it fit (✓ or ✗). The fit keeps the largest size that fits, so each ✓ should be smaller than every ✗. |
https://bw.teddysc.me/#Wi-Fi:%20**Cafe%20Guest**%0APassword:%0A**latte;art**&dbg=3
The overlay shows only in the viewer, not in the editor's preview. A screenshot of it at level 3 is the most useful thing to include when reporting a display problem.
The editor
The editor shows a live preview that renders exactly what the viewer shows, plus controls for every parameter.
- The address bar always mirrors the current state, without filling your history.
- Editing the fragment in the address bar updates the controls.
- The URL field below the preview is editable too: paste a link (or just its fragment) and press Enter to load it. In the installed app, which has no address bar, that's how to edit an existing link.
- Copy URL copies the viewer URL (the same fragment without
/editor). Open viewer shows it in the same tab, and Back returns to the editor; the icon next to it opens it in a new tab instead. The QR button next to the URL shows it as a QR code, so a phone or tablet can scan it to open the display. - Parameters left at their defaults are dropped from the URL to keep it short.
Install and offline
BIGWORDS.PAGE can be installed as an app from the browser: Install in Chrome or Edge, or Share → Add to Home Screen on iPhone and iPad. The app opens on the editor, fullscreen where the device allows it.
After the first visit, the site keeps a copy of itself, so displays, the editor and these docs work without a connection, whether installed or in a browser tab. Images from other sites (img) only show while online. When you're online, pages always load the latest version. Browsers can clear the copy for a site you haven't used in a while, especially in a tab; an installed app keeps it longer.
Privacy
The message and settings live only in the URL fragment, which browsers do not send to servers. BIGWORDS.PAGE's own code adds no analytics, cookies or tracking. A site that hosts it may add analytics of its own: check that site's notes, which may follow at the end of this page, or its privacy policy.
Self-hosting
The build is a folder of static files. Any static host works if it serves index.html for /, editor.html for /editor, docs.html for /docs, and 404.html with status 404 for any other path. Set SITE_URL to your own origin when building (for example SITE_URL=https://signs.example.com npm run build) to add canonical URLs, social cards and a sitemap. A Docker image based on Caddy is included in the repository:
docker compose up -d --build
The container listens on port 80 inside its network and publishes no ports by default. Put it behind your reverse proxy, which handles TLS.