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.

 RegisterSelf-host
Best forMost site ownersDevelopers
Run a server?No, it's hosted for youYes, your own
What you doFill a short form, paste a snippetDeploy and configure
CodingNoneSome

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:

  1. Open the registration page and enter your website's address.
  2. Click create. You get two things: a snippet and an admin token.
  3. 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:

  1. Open your own site so you show up as a live visitor. Add #townsquare-owner to the address and the square shows a hint with your visitor number (You're visitor #N …).
  2. On your admin page, find that visitor in the active list.
  3. 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:

TokenControls
--sceneBackground
--pageGround
--surfaceButtons and tags
--inkText and line work
--youAccent (your own character)
--tree-trunkTree trunks
--tree-canopyTree leaves
--otherOther line work
--groundGround 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 /healthz for 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:

VariablePurpose
HOST / PORTWhere the server listens.
PUBLIC_ORIGINPublic origin used in generated snippets and admin links.
DATA_DIRWhere the registered-site list is stored (defaults to .data/).
SERVICE_ADMIN_PASSWORDEnables the service admin page.
REGISTRATIONS_PER_HOURPer-IP registration limit (default 20; 0 disables).
AUTH_FAILURES_PER_HOURPer-IP failed sign-in throttle (default 30; 0 disables).
INACTIVE_DISCONNECT_MSDrop away/inactive visitors (default 30 min; 0 disables).
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_IDGet 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:

OptionPurpose
serverOriginThe TownSquare origin to load assets from and connect to.
socketPathWebSocket path on that origin (defaults to /live).
siteKeyOnly needed when one hosted server runs several registered sites.
themeUse "host" to follow common host-page dark mode signals. Defaults to "auto".
connectionsNeighbouring 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.