TownSquare documentation
Add a little life to your website
TownSquare is a small shared place you drop into your site. Visitors can see each other, wander around, and say hello in real time. This guide covers how to add one and look after it. You don't need to be technical to follow along.
Two ways to use it
There are two ways to put a TownSquare on your site. Most people want the first one.
| Register | Self-host | |
|---|---|---|
| Best for | Most site owners | Developers |
| Run a server? | No, it's hosted for you | Yes, your own |
| What you do | Fill a short form, paste a snippet | Deploy and configure |
| Coding | None | Some |
Register is the easy path. You don't run anything yourself. You enter your website's address and copy a small snippet into your page. The live square is hosted for you. The Get started section below is about this path.
Self-hosting means running the TownSquare server yourself. You get full control, but it takes some setup and a bit of code. If that sounds like you, jump to Self-hosting.
Get started
Adding TownSquare takes about a minute:
- Open the registration page and enter your website's address.
- Click create. You get two things: a snippet and an admin token.
- Paste the snippet into your page, wherever you want the square to appear.
That's it. Your square is live, and visitors start showing up.
Keep your admin token somewhere safe. It's how you moderate the square later. There's no account behind it and no way to recover it.
Manage your square
Open your admin page and sign in with your admin token. From there you can:
- see whether your site is connected, and who's around right now
- copy the embed snippet again any time
- customize the colors and scene to match your site (see Customize the look)
- mark yourself as the verified site owner to get a badge (see below)
- keep things friendly by removing a visitor, turning off chat, or pausing the square
- clear recent in-memory chat messages
Moderation changes take effect live. Disabling the site closes the square for everyone; turning chat back on or re-enabling the site brings it back.
Mark yourself as the site owner
Visitors are anonymous, so by default nothing tells the owner apart from everyone else. You can give your own character a verified badge — a 👑 crown:
-
Open your own site so you show up as a live visitor. Add
#townsquare-ownerto the address and the square shows a hint with your visitor number (You're visitor #N …). - On your admin page, find that visitor in the active list.
- Click Make owner. Your character gets the crown live for everyone in the square, and keeps it on every future visit from that browser. Click Owner ✓ to remove it.
The badge is issued by the server, so it can't be faked by typing a name or picking a color. It's tied to the specific browser you marked, so another browser can't claim it. Because TownSquare stays accountless, a new device or cleared browser storage means marking owner once more — one click. You can mark more than one browser if you want the badge on several devices.
Customize the look
Out of the box your square uses TownSquare's default look — a warm, neutral palette that already follows light and dark mode. You don't have to change anything. When you want it to feel more like your site, your admin page has a customization panel with a live preview.
Scene
Choose how many benches, trees, lamps, and birds appear, and where they sit. Scene changes are saved on the TownSquare server and tied to your site key, so they take effect live — anyone in your square sees the new layout right away, and you never touch the snippet on your site.
Colors
Pick a color for each part of the scene — background, ground, buttons and tags, ink, accent, and the tree trunk and leaves — and tune light and dark separately. As you edit, the admin page builds a ready-made Customization CSS block from your choices. Copy it into your site's stylesheet once, and your square wears your colors.
Colors travel as that small CSS block on your own page, not through the snippet. If you change colors later, copy the updated block again. Scene props (the benches, trees, and so on) are the part that updates live.
Bring your own CSS (advanced)
Hosted styling is the easy path, not a lock-in. If you want more control
than the swatches give you, edit the Customization CSS — or write your own
— targeting the same visual tokens on #townsquare-root. They
apply to both light and dark:
| Token | Controls |
|---|---|
--scene | Background |
--page | Ground |
--surface | Buttons and tags |
--ink | Text and line work |
--you | Accent (your own character) |
--tree-trunk | Tree trunks |
--tree-canopy | Tree leaves |
--other | Other line work |
--ground | Ground line |
Your stylesheet always wins. Hosted embeds never write these tokens
inline, so any rule you set on #townsquare-root overrides the
default look — technical owners get full control while everyone else stays
on the simple path.
For developers
Self-hosting
Everything below is optional. Most people are better off registering, which needs no server. Self-hosting is for developers who want to run TownSquare on their own infrastructure.
You're deploying one small Node process (Node.js 18 or newer). It does three things:
- serves the widget and the registration and admin pages
- handles realtime presence and chat over a WebSocket
- answers
/healthzfor health checks
It defaults to host 127.0.0.1, port 8787, and WebSocket path /live.
Run the server
Locally
npm install
npm start
With Docker
docker build -t townsquare .
docker run --rm -p 8787:8787 -e HOST=0.0.0.0 townsquare
Once it's running, your own /register page works just like the
hosted one: site owners register, get a snippet and admin token, and you can
manage every site from /service-admin (set
SERVICE_ADMIN_PASSWORD to enable it).
Configuration
Everything is set through environment variables. The common ones:
| Variable | Purpose |
|---|---|
HOST / PORT | Where the server listens. |
PUBLIC_ORIGIN | Public origin used in generated snippets and admin links. |
DATA_DIR | Where the registered-site list is stored (defaults to .data/). |
SERVICE_ADMIN_PASSWORD | Enables the service admin page. |
REGISTRATIONS_PER_HOUR | Per-IP registration limit (default 20; 0 disables). |
AUTH_FAILURES_PER_HOUR | Per-IP failed sign-in throttle (default 30; 0 disables). |
INACTIVE_DISCONNECT_MS | Drop away/inactive visitors (default 30 min; 0 disables). |
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID | Get a Telegram ping on each chat message. |
For local runs, copy .env.example to .env; the
server loads it on startup. Real environment variables win over
.env values.
Embed in code
If you'd rather mount TownSquare yourself instead of using the registration flow, load the stylesheet and module, then mount it into any element:
<link rel="stylesheet" href="https://your-townsquare-host/widget.css" />
<div id="townsquare-root"></div>
<script type="module">
import { mountTownSquare } from "https://your-townsquare-host/townsquare.mjs";
mountTownSquare(document.getElementById("townsquare-root"), {
serverOrigin: "https://your-townsquare-host",
socketPath: "/live",
theme: "host"
});
</script>
Your page decides where the square appears. TownSquare takes care of what happens inside it. The mount options are intentionally small:
| Option | Purpose |
|---|---|
serverOrigin | The TownSquare origin to load assets from and connect to. |
socketPath | WebSocket path on that origin (defaults to /live). |
siteKey | Only needed when one hosted server runs several registered sites. |
theme | Use "host" to follow common host-page dark mode signals. Defaults to "auto". |
connections | Neighbouring towns linked at the edges, e.g. [{ side: "left", label: "Maple Lane", url: "https://maplelane.dev" }]. Each grows a signpost visitors can walk over to. |
The registration snippet above is the standard way to embed a hosted site.
It keeps TownSquare in sync with common host-page dark mode signals
such as html.dark, data-theme, and
data-color-mode.
To restyle a self-hosted square, set the palette tokens on
#townsquare-root in your own stylesheet — see
Customize the look for the full list. The widget
writes no inline palette styles, so your CSS always wins.
Production notes
For a real deployment, put TownSquare behind a reverse proxy (nginx, Caddy, or similar) that:
- forwards normal HTTP traffic
- allows WebSocket upgrades on
/live - keeps the origin stable so asset and socket URLs line up
On a custom socket path like /townsquare/live, pass it
explicitly with socketPath. The repo ships a deploy helper at
scripts/deploy.sh (copy .env.deploy.example to
.env.deploy.local first). By default it deploys the local
production tag. Use --tag <git-tag> for another
tag, and use --ref only when deploying an explicit branch or
commit. The server also exposes a health endpoint at
/healthz that simply returns ok.
Self-hosting is kept lean on purpose. There are no accounts, no token recovery, and no billing. The goal is a single-site server that's clean and easy to run, with the hosted model layered on top later.