Daily active players, average session length, and retention, measured from real sessions, including players who never sign in.
There is no analytics setup. Creating the browser client starts it:
<script src="https://strafe.fun/js/strafe.js"></script>
<script>
new Strafe({ appId: 'your-app-id' });
</script>From that one line the SDK:
strafe.progress('game_start') the moment a player can actually play and make it the first step of your funnel: the median time under it is your time-to-playable, and the drop before the next step is everyone who opened the game and left while it was still getting ready. Without that step your funnel begins wherever your first milestone is, and every player you lost before it is missing from the report rather than counted as a loss.A cohort needs a finished day to land on
Day-1 retention for yesterday's new players would be judged on today, which is still running, everyone who hasn't come back yet would score as churned. So the two most recent days read pending rather than zero, and fill in once the day is over.GET /api/me/creations/[gameId]/analytics?days=14&population=all, with your portal session. Returns dense daily buckets, every day in the window, including the empty ones.Counting anonymous players means storing a device id in their browser, so the SDK asks or tells them itself. An embedding game can't be relied on to mention it in its own privacy policy. Set the mode when you create the client:
new Strafe({ appId: 'your-app-id', consent: 'auto' }); // default
new Strafe({ appId: 'your-app-id', consent: 'manual' }); // your own UI
new Strafe({ appId: 'your-app-id', consent: 'off' }); // you handle compliancestrafe.optIn() and strafe.optOut(); read the current state with strafe.getConsent() ('granted' | 'denied' | 'unknown'). Guests in the EU/UK aren't counted until they opt in.Check it worked
1. Open the sandbox with your App ID. Session recorded should turn green within a second or two; leave the tab open a minute and the duration starts climbing, which is the heartbeat working.
2. From your own game, ask the same question of the session in front of you:
await fetch('https://strafe.fun/api/sdk/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId: strafe.getAppId(),
sessionId: strafe.getSessionId(),
deviceId: strafe.getDeviceId(),
}),
}).then(r => r.json());3. Open your game's Dashboard tab. Your visit shows up the same UTC day; day-1 retention for it stays pending until that day is over, which is correct, not a bug.
If nothing arrives
session: null from the check above means we never received it. In order of likelihood: the App ID is wrong or its keys were deleted; the player declined analytics (getConsent() returns 'denied', or Do-Not-Track is on, and getSessionId() is null as a result); or the request never left the browser. Check the console and your Content-Security-Policy.