SDK Reference
Reference for @looploop/sdk. Import the LoopLoop namespace and call methods on its submodules (auth, telemetry, feedback, etc.).
Quick Start
import { LoopLoop } from '@looploop/sdk';
const session = await LoopLoop.auth.getSession();
const sessionId = crypto.randomUUID();
LoopLoop.telemetry.startSession(sessionId, '0.1.0');
startGame();
Call LoopLoop.telemetry.startSession early so telemetry, the feedback button, and error capture behave as designed. Use LoopLoop.init when you need to set API base URL, game slug, or auth mode.
LoopLoop.auth
getSession(): Promise<PlayerSession | null>
Returns the signed-in player session, or null for guests and when no session exists (including failed network requests).
login(provider: 'google' | 'discord'): Promise<void>
Starts OAuth with Google or Discord (redirect or popup depending on SDK config).
logout(): Promise<void>
Ends the server session and clears local auth state (including cached session).
isGuest(): boolean
Returns true when there is no cached session (guest). After getSession() resolves, behavior aligns with whether a session was returned.
Example
const session = await LoopLoop.auth.getSession();
if (!session) {
// optional: LoopLoop.auth.login('google');
}
LoopLoop.telemetry
startSession(sessionId: string, gameVersion: string): void
Begins a play session: queues a session_start event, starts a 10s flush interval, initializes error capture, and auto-mounts the feedback button.
emit(event: TelemetryEvent): void
Appends a gameplay event to the batch. Batches flush when 20 events are queued or on the periodic timer (~10s).
endSession(data?: { durationSeconds, enemiesKilled, levelReached, causeOfDeath }): void
Ends the session: emits session_end, clears timers, stops error capture, unmounts the feedback button, and flushes remaining events. Pass optional summary fields in data for analytics.
flush(): void
Force-sends the current event batch.
Example
LoopLoop.telemetry.emit({
type: 'enemy_killed',
timestamp: Date.now(),
data: { enemyId: 'boss-1' },
});
await LoopLoop.telemetry.endSession({
durationSeconds: 120,
enemiesKilled: 42,
levelReached: 3,
causeOfDeath: 'spikes',
});
LoopLoop.feedback
showQuickPrompt(opts: { sessionId, gameVersion, sessionMetrics? }): void
Shows the one-tap rating UI: Too Easy, Just Right, Too Hard, Boring, Buggy. Submits on tap; optional sessionMetrics attach run summary.
showDetailedForm(opts?: { gameVersion, context? }): void
Opens the full feedback form (category + text). Signed-in users get the full flow; guests may see a sign-in prompt depending on configuration.
showCrashReport(error: Error): void
Shows the crash-report UI (e.g. after an uncaught exception).
submit(payload: FeedbackPayload): Promise<void>
POSTs feedback JSON to the platform. Include sessionId and gameVersion; other fields depend on quick vs detailed feedback.
Example
await LoopLoop.feedback.submit({
sessionId,
gameVersion: '0.1.0',
quickRating: 'just_right',
sessionMetrics: {
durationSeconds: 90,
enemiesKilled: 10,
levelReached: 2,
causeOfDeath: '',
},
});
LoopLoop.feedbackButton
mount(): void
Mounts the draggable floating feedback button (typically called for you from telemetry.startSession).
unmount(): void
Removes the button and listeners.
LoopLoop.notifications
getUnread(): Promise<Notification[]>
Returns unread notifications for the current user (empty array on error).
markRead(id: string): Promise<void>
Marks a notification as read by id.
getLatestRelease(): Promise<Release | null>
Fetches the latest platform release notes, or null if none / error.
LoopLoop.capture
startRecording(config?: { canvas?, interval?, duration?, width?, height?, quality? }): void
Records frames from the game canvas (default: find canvas under #game-container or first canvas) for GIF encoding.
stopRecording(): Promise<Blob>
Stops recording and returns a GIF Blob.
captureFrame(config?: { canvas?, width? }): Promise<Blob>
Captures a single PNG frame.
uploadThumbnail(gameId: string, blob: Blob, type: 'gif' | 'png'): Promise<string>
Uploads a thumbnail asset; returns the public URL.
uploadSplash(gameId: string, blob: Blob): Promise<string>
Uploads a wide landscape splash / hero image (server enforces max 5 MB); returns the public URL.
Example
LoopLoop.capture.startRecording({ width: 480, interval: 200 });
// ... gameplay ...
const gif = await LoopLoop.capture.stopRecording();
const gameId = await LoopLoop.gameContext.getGameId();
if (gameId) {
await LoopLoop.capture.uploadThumbnail(gameId, gif, 'gif');
}
// Wide landscape splash (PNG), max 5 MB on server
const splashPng = await LoopLoop.capture.captureFrame({ width: 1920 });
if (gameId) {
await LoopLoop.capture.uploadSplash(gameId, splashPng);
}
LoopLoop.errorCapture
init(): void
Starts listening for uncaught errors and unhandled rejections, and a >5s main-thread freeze watchdog (via requestAnimationFrame). Automatically started when you call telemetry.startSession.
stop(): void
Tears down listeners and the watchdog.
LoopLoop.gameContext
getGameSlug(): string
Returns the game slug from subdomain ({slug}.looploop.gg), path (/games/{slug}), or SDK config.
getGameId(): Promise<string>
Resolves and caches the game id from the slug via the API.
LoopLoop.profile
getProfile(playerId: string): Promise<PublicProfile>
Loads a public player profile by id.
promotionSeen(): Promise<void>
Dismisses the tier promotion ceremony server-side after the player has seen it.
LoopLoop.games
list(params?: { genre?, page?, limit? }): Promise<{ games, total, page }>
Browse games with optional filters and pagination.
create(brief: GameBrief): Promise<{ gameId }>
Creates a new game from a design brief; returns the new gameId (and related fields from the API).
toggleFavorite(gameId: string): Promise<{ isFavorited }>
Toggles the current user’s favorite for a game; response includes the new favorite state.
Example
const { games, total, page } = await LoopLoop.games.list({
genre: 'action',
page: 1,
limit: 12,
});
Types
Key types exported from @looploop/sdk (see PlayerSession and related in the package).
PlayerSession
interface PlayerSession {
id: string;
displayName: string;
email: string | null;
avatarUrl: string | null;
authProvider: 'google' | 'discord';
tier: PlayerTier;
creatorTier: CreatorTier | null;
loopBalance: number;
totalLoopsEarned: number;
tierProgress: TierProgress;
pendingPromotion: PendingPromotion | null;
}
TelemetryEvent
interface TelemetryEvent {
type: string;
timestamp: number;
data: Record<string, unknown>;
}
FeedbackPayload
interface FeedbackPayload {
gameId?: string;
sessionId: string;
gameVersion: string;
quickRating?: 'too_easy' | 'just_right' | 'too_hard' | 'boring' | 'buggy';
category?: 'balance' | 'content' | 'bug' | 'art' | 'ux' | 'other';
text?: string;
sessionMetrics?: {
durationSeconds: number;
enemiesKilled: number;
levelReached: number;
causeOfDeath: string;
};
}
Notification
interface Notification {
id: string;
type: 'feedback_implemented' | 'release' | 'tier_promoted' | 'creator_tier_promoted' | 'top5_entered' | 'top5_left';
title: string;
body: string;
feedbackId: string | null;
releaseId: string | null;
gameId: string | null;
createdAt: string;
readAt: string | null;
}
Release
interface Release {
id: string;
version: string;
notesMarkdown: string;
createdAt: string;
}
GameState
interface GameState {
status: 'loading' | 'menu' | 'playing' | 'paused' | 'game_over' | 'upgrade_select' | 'feedback';
sessionId: string | null;
elapsedSeconds: number;
gameVersion: string;
player: {
x: number;
y: number;
health: number;
maxHealth: number;
level: number;
xp: number;
xpToNextLevel: number;
alive: boolean;
} | null;
entities: {
enemies: Array<{ id: string; type: string; x: number; y: number; health: number }>;
pickups: Array<{ id: string; type: string; x: number; y: number }>;
};
metrics: {
enemiesKilled: number;
damageDealt: number;
damageTaken: number;
itemsCollected: number;
levelsGained: number;
};
}
PublicProfile
interface PublicProfile {
id: string;
displayName: string;
avatarUrl: string | null;
tier: PlayerTier;
creatorTier: CreatorTier | null;
earningsLabel: EarningsLabel;
feedbackStats: {
submitted: number;
implemented: number;
implementationRate: number;
};
gamesCreated: string[];
recentImplemented: Array<{
feedbackText: string;
changeSummary: string;
gameTitle: string;
version: string;
}>;
}
Supporting enums / unions such as PlayerTier, CreatorTier, TierProgress, and PendingPromotion are defined alongside these in the SDK.