PlaycademyPlaycademy

Browser

Client-side SDK for browser environments

Overview

The browser SDK provides namespaces for interacting with the Playcademy platform from your project frontend.

All methods are accessed through the PlaycademyClient instance.

import { PlaycademyClient } from '@playcademy/sdk'

const client = await PlaycademyClient.init()

const user = await client.users.me() // get current user
const balance = await client.credits.balance() // get current balance
await client.scores.submit(gameId, 1500, { level: 5 }) // submit score

Initialization

PlaycademyClient.init() automatically detects your environment and configures the client accordingly.

See SDK Initialization for details.

For complete documentation including all types and method signatures: SDK Reference


Core Namespaces

client.users

Retrieve current user information and manage player inventory.

// Get current user
const user = await client.users.me()

// Get inventory
const inventory = await client.users.inventory.get()

// Add items
await client.users.inventory.add('sword-123', 1)

// Remove items
await client.users.inventory.remove('potion-456', 3)

client.credits

Manage platform currency (credits).

// Check balance
const balance = await client.credits.balance()

// Add credits
await client.credits.add(100)

// Spend credits
await client.credits.spend(50)

client.scores

Submit scores for your project.

// Submit a score
const result = await client.scores.submit(gameId, 1500, {
    level: 5,
    difficulty: 'hard',
    perfectRun: true,
})

client.identity

Connect external identity providers (Google, Discord, etc.) to user accounts.

// Connect Google account
const result = await client.identity.connect({
    provider: 'google',
    callbackUrl: '/auth/callback',
})

if (result.success) {
    console.log('Connected:', result.user)
}

Integration Namespaces

client.timeback

Track learning activities with automatic XP calculation. Access user context for content gating.

user

Access the user's Timeback context via client.timeback.user:

Example
const id = client.timeback.user.id // User's Timeback ID
const role = client.timeback.user.role // 'student' | 'parent' | 'teacher' | ...
const enrollments = client.timeback.user.enrollments // App-scoped course enrollments
const orgs = client.timeback.user.organizations // App-scoped organizations

// Fetch fresh data from server (cached for 5 min)
const fresh = await client.timeback.user.fetch()
const forced = await client.timeback.user.fetch({ force: true })

App-Scoped

Enrollments and organizations are filtered to courses defined in your playcademy.config.js.

startActivity

Start tracking an activity. Only activityId is required:

Example
// Minimal (most common)
client.timeback.startActivity({
    activityId: 'math-quiz-1', // automatically derived to "Math Quiz 1"
})

// With custom name override
client.timeback.startActivity({
    activityId: 'math-quiz-1',
    activityName: 'Advanced Multiplication Quiz',
})

Auto-filled Metadata

The SDK automatically fills in metadata from your project config:

  • activityName: Derived from activityId ("math-quiz-1" → "Math Quiz 1")
  • appName, subject, sensorUrl: From playcademy.config.{js,json}

You can override any of these by providing them explicitly.

endActivity

End the current activity and submit results:

Example
// Auto-calculate XP based on score
await client.timeback.endActivity({
    correctQuestions: 8,
    totalQuestions: 10,
})

// Override XP calculation
await client.timeback.endActivity({
    correctQuestions: 8,
    totalQuestions: 10,
    xpAwarded: 15, // award exactly 15 XP
})

{pause,resume}Activity

Pause the timer during instructional moments and resume when ready:

Example
client.timeback.startActivity({ activityId: 'math-quiz-1' })

// Student attempts problems...

if (studentAnswerWrong) {
    // Pause timer to show correct answer
    client.timeback.pauseActivity()

    // Let student learn from mistake
    showCorrectAnswer()

    // Resume when they continue playing
    client.timeback.resumeActivity()
}

// End activity (only active problem-solving time counted)
await client.timeback.endActivity({
    correctQuestions: 8,
    totalQuestions: 10,
})

See Timeback Integration for complete documentation.

client.backend

Call your backend API routes.

These methods connect to the server-side routes you create in your server/api/ directory.

Learn more here.

Always Use sdk.backend

Don't use plain fetch() for your backend routes.

The SDK automatically includes the platform authentication token, which is required for c.get('playcademyUser') to work in your routes.

// ✅ Correct - includes platform token
const data = await client.backend.get('/my-route')

// ❌ Wrong - playcademyUser will be null
const data = await fetch('/api/my-route').then(r => r.json())

get

Make GET requests:

const data = await client.backend.get('/hello')
console.log(data.message)

// With custom headers
const data = await client.backend.get('/protected', {
    'X-Custom-Header': 'value',
})

post / put / patch

Make requests with body data:

// POST request
const result = await client.backend.post('/validate', {
    answer: 'paris',
})

// PUT request
await client.backend.put('/settings', {
    volume: 0.8,
})

// PATCH request
await client.backend.patch('/profile', {
    displayName: 'NewName',
})

delete

Delete resources:

await client.backend.delete('/cache/clear')

download

Download binary files:

const response = await client.backend.download('/files?key=report.pdf')
const blob = await response.blob()

// Trigger browser download
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'report.pdf'
a.click()
URL.revokeObjectURL(url)

Downloading Files

The download method returns a raw Response object, allowing you to access the blob, streams, or other binary data.

url

Build URLs for HTML elements (images, videos, etc.):

// Tagged template literal (recommended)
const imageUrl = client.backend.url`/assets/${sprite.key}`
<img src={imageUrl} />

// Regular function call
const videoUrl = client.backend.url('/videos/intro.mp4')
<video src={videoUrl} />

Perfect for catchall routes that serve files by path.

See Custom Routes and Bucket Storage for more examples.


Runtime & Lifecycle

client.runtime

Game lifecycle events, messaging, and static asset loading.

// Signal ready
client.runtime.ready()

// Exit
client.runtime.exit()

// Listen for pause
client.runtime.onPause(() => {
    pauseGame()
})

// Listen for resume
client.runtime.onResume(() => {
    resumeGame()
})

// Load static assets at runtime
const levelData = await client.runtime.assets.json`levels/level-${id}.json`
img.src = client.runtime.assets.url`badges/${badgeType}.png`
audio.src = client.runtime.assets.url`sfx/${soundEffect}.wav`

Connection Monitoring

The platform automatically monitors connection health and shows baseline alerts to users.

Your application only needs to handle the logic for what to do when a user's connection is degraded or lost.

Platform Shows Baseline Alerts

The platform automatically shows connection alerts ("Connection Lost", "Slow Connection", etc.).

Your onDisconnect handler is just for app-specific logic.

Basic Example

const client = await PlaycademyClient.init({
    onDisconnect: async ({ state, displayAlert }) => {
        if (state === 'offline') {
            // Save critical data and return to safe location
            await saveToLocalStorage(appState)
            returnToLobby()

            // Optional: Add app-specific context
            displayAlert('Progress saved locally. Returning to lobby...', { type: 'info' })
        }
    },
})

Pause App on Connection Issues

const client = await PlaycademyClient.init({
    onDisconnect: ({ state }) => {
        if (state === 'offline' || state === 'degraded') {
            pause()
            showReconnectingOverlay()
        }
    },
})

client.on('connectionChange', ({ state }) => {
    if (state === 'online') {
        hideReconnectingOverlay()
        resume()
    }
})

API Reference

Connection states:

StateDescription
onlineConnection is healthy
offlineNo connectivity
degradedSlow/unstable connection

Methods:

MethodDescription
client.getConnectionState()Get current connection state
client.checkConnection()Force immediate connection check
client.onDisconnect(handler)Register disconnect handler
client.on('connectionChange', handler)Listen to connection state changes

Display custom alerts:

Use displayAlert() to show a toast notification in the top-left corner.

This is the same mechanism and location the platform uses for automatic network degradation alerts.

displayAlert('Custom message', {
    type: 'error', // 'info' | 'warning' | 'error'
    duration: 5000, // Auto-dismiss in ms (optional)
})

Disable monitoring:

const client = await PlaycademyClient.init({
    enableConnectionMonitoring: false,
})

Event System

Listen for platform events:

// Pause event
client.on('pause', () => {
    pause()
})

// Resume event
client.on('resume', () => {
    resume()
})

// Credits changed
client.on('creditsChanged', (balance: number) => {
    updateBalanceUI(balance)
})

// Item purchased
client.on('itemPurchased', (item: ItemWithId) => {
    showItemAcquired(item)
})

// Level up
client.on('levelUp', (status: LevelStatus) => {
    showLevelUpAnimation(status)
})

// Exit requested
client.on('exit', () => {
    cleanup()
})

What's Next?

On this page