Public Val Town analytics service:
Live service:
valtown_project_analytics is a reusable analytics service for lightweight event tracking across multiple Val Town projects.
It provides:
This is intentionally small and pragmatic. It is meant for meaningful product events, not full session replay or enterprise analytics workflows.
GET /
Human-readable landing page with usage notesGET /client.js
Reusable browser clientPOST /track
Event ingest endpointGET /summary?project=<name>
Per-project summary with unique sessions, totals, and recent eventsGET /events?project=<name>&limit=25
Recent per-project event streamOPTIONS *
CORS preflight handlingEvents are stored in a shared SQLite table named events.
Columns:
idprojecteventsession_idpage_pathpage_urlreferrersourceuser_agenttsreceived_atprops_jsonIndexes:
idx_events_project_tsidx_events_project_event_ts<script src="https://pchinjr--2dfdceb4239511f1baa142dde27851f2.web.val.run/client.js" defer></script> <script defer> window.addEventListener("DOMContentLoaded", function () { const analytics = window.ValTownAnalytics.init({ project: "my-project", globalProps: { app: "my-project", }, }); analytics.track("page_viewed", { screen: "landing" }); }); </script>
window.ValTownAnalytics.init(config) accepts:
projectendpoint optional overrideglobalProps optional object merged into every eventdebug optional booleanIt returns:
track(event, props)sessionId()Behavior:
localStoragenavigator.sendBeacon when availablefetch with keepalive{ "project": "guess-that-hype", "event": "quiz_started", "sessionId": "vt-...", "pagePath": "/", "pageUrl": "https://example.web.val.run/", "referrer": "https://example.com/", "source": "browser", "ts": "2026-03-19T13:00:00.000Z", "props": { "runId": "...", "questionsPerRun": 8 } }
page_viewedquiz_startedquestion_answeredquiz_completedshare_clickedshare_copiedcta_clickedGood fit for:
Not designed for:
main.ts
Full analytics service implementationREADME.md
Project documentation