@oasiz/sdk
v1.8.9
Published
Typed SDK for Oasiz game platform bridge APIs.
Keywords
Readme
Oasiz game SDKs
Games on Oasiz can integrate using either of these official SDKs:
| Platform | Package | Use for |
| --- | --- | --- |
| HTML5 / TypeScript | @oasiz/sdk (npm) | Canvas, Phaser, custom JS/TS, any browser game |
| Unity WebGL | Unity runtime in this repo (packages/OasizSDK/) | Unity projects targeting WebGL |
Both talk to the same host bridges (window.submitScore, __oasizLeaveGame, layout APIs, custom DOM events such as oasiz:pause, etc.). Unsupported hosts no-op safely; local dev usually logs warnings instead of crashing.
HTML5 / TypeScript (@oasiz/sdk)
Typed SDK for integrating browser games with the Oasiz platform: score, haptics, cross-session state, multiplayer hooks, layout (safe area, viewport insets, leaderboard visibility), local app simulation, graphics performance, navigation (back / leave), and lifecycle events.
Install
npm install @oasiz/sdkThe published package includes ESM, CommonJS, and TypeScript declarations.
Quick start
import { oasiz } from "@oasiz/sdk";
// 0. Optional local app preview for web development
if (import.meta.env.DEV) {
oasiz.enableAppSimulator();
}
// 1. Load persisted state at the start of each session
const state = oasiz.loadGameState();
let level = typeof state.level === "number" ? state.level : 1;
// 2. Save state at checkpoints
oasiz.saveGameState({ level, coins: 42 });
// 3. Trigger haptics on key events
oasiz.triggerHaptic("medium");
// 4. Respect the host's top safe area (percent of viewport height → CSS vh)
document.documentElement.style.setProperty(
"--safe-top",
`${oasiz.safeAreaTop}vh`,
);
// 5. Pick graphics settings for this device
const graphics = oasiz.getGraphicsPerformance();
renderer.setPixelRatio(
graphics.tier === "high" ? 1.5 : graphics.tier === "medium" ? 1.25 : 1,
);
// 6. If launched from a platform lobby room, read the frozen room payload
const launch = oasiz.getLaunchContext();
const modeId = launch?.modeId ?? "solo";
const players = launch?.players ?? [];
// 7. Submit score when the game ends
oasiz.submitScore(score);
// 8. Optionally hide the leaderboard while a custom overlay is open
oasiz.setLeaderboardVisible(false);
// 9. Optionally surface console logs in-game while debugging
oasiz.enableLogOverlay({
enabled: new URLSearchParams(window.location.search).has("oasizLogs"),
collapsed: true,
});Local app simulator
oasiz.enableAppSimulator(options?: AppSimulatorOptions): AppSimulatorHandle
Opt-in local web helper for previewing a game inside Oasiz-style mobile chrome. It simulates the app back button, leaderboard pill, like/comment hub, comments modal, and leaderboard modal while also injecting local safe-area and viewport-inset bridge values.
if (import.meta.env.DEV) {
const appPreview = oasiz.enableAppSimulator({
device: "iphone-17-pro-max",
likes: 2400,
comments: 18,
score: 12400,
});
debugCommentsButton.onclick = () => appPreview.openComments();
debugLeaderboardButton.onclick = () => appPreview.openLeaderboard();
}The simulator includes the back-button test bridge, so Escape, browser Back, and the simulated app back button route through the same oasiz.onBackButton(...) handler when back override is active. Use enableBackButtonTesting() directly only when you want back simulation without app chrome.
Set frame: false if your own dev harness already renders the game in a phone frame.
Platform lobby rooms
Oasiz-owned platform lobbies are the default multiplayer shape. Games do not create room lists, invite screens, ready buttons, or start buttons inside the game runtime. The platform opens a lobby for the selected game, lists waiting rooms, lets the user create or join a room, collects mode/settings/readiness, and starts the game only after the host decides to launch.
The game declares its lobby contract in publish.json:
{
"title": "Space Force",
"description": "Squad arena combat",
"category": "action",
"multiplayer": {
"kind": "platform-lobby",
"schemaVersion": 1,
"minPlayers": 1,
"maxPlayers": 6,
"defaultModeId": "classic",
"readyPolicy": "all_non_host",
"transport": { "type": "custom" },
"modes": [
{
"id": "classic",
"label": "Classic",
"minPlayers": 1,
"maxPlayers": 6,
"settingsSchema": {
"durationMinutes": {
"type": "integer",
"min": 3,
"max": 20,
"default": 10
}
}
}
]
}
}When the host starts the room, Oasiz injects a frozen launch context:
const launch = oasiz.getLaunchContext();
if (launch) {
console.log(launch.roomCode, launch.modeId, launch.settings);
console.table(launch.players);
}oasiz.getLaunchContext() returns null in local development or single-player launches. Existing helpers such as oasiz.gameId, oasiz.roomCode, oasiz.playerId, oasiz.playerName, and oasiz.playerAvatar also fall back to the launch context when legacy globals are not injected.
The launch context includes:
gameId,gameVersionId,roomId,roomCode, andsessionIdhostUserIdandlocalPlayerId- selected
modeId - validated
settings - frozen
playersroster with slot indexes, readiness, teams/roles, and bot metadata transportconfig for the game runtime to connect to its actual multiplayer transport
For Unity WebGL, use OasizSDK.GetLaunchContext() or OasizSDK.GetLaunchContextJson(). Arbitrary settings and transport objects are exposed as settingsJson and transportJson so Unity projects can parse them with their preferred JSON library.
Player character animations
Use the Jibble animation constants when wiring a player character atlas so you can reference known animation IDs during local development instead of publishing first and reading logs.
The atlas animation IDs are stable strings. Directional animations use the pattern
<action>_<direction>, where the direction suffix is one of n, ne, e, se,
s, sw, w, or nw.
| Player-facing direction | Compass direction | Suffix |
| --- | --- | --- |
| front, forward, forth | South | s |
| front-right, forward-right | SouthEast | se |
| right | East | e |
| back-right, backward-right | NorthEast | ne |
| back, backward | North | n |
| back-left, backward-left | NorthWest | nw |
| left | West | w |
| front-left, forward-left | SouthWest | sw |
You can also pass compass names directly, such as north, south, north-east,
or southwest.
Animation catalog
| Action | Constant | Animation IDs |
| --- | --- | --- |
| Idle | JIBBLE_ANIMATION.Idle | idle_n, idle_ne, idle_e, idle_se, idle_s, idle_sw, idle_w, idle_nw |
| Walk | JIBBLE_ANIMATION.Walk | walk_n, walk_ne, walk_e, walk_se, walk_s, walk_sw, walk_w, walk_nw |
| Backflip | JIBBLE_ANIMATION.Backflip | backflip |
backflip is not directional, so getJibbleAnimationId("backflip", "left")
still returns backflip.
Local browser sample atlas
Inside the Oasiz app, oasiz.getPlayerCharacter() returns the authenticated
player's real character atlas. On localhost or file:// without the app
bridge, it first fetches an Oasiz-owned real sample atlas from
https://api.oasiz.ai/api/sdk/sample-character, so browser games can test
sprite loading, atlas slicing, and animation lookup before publishing.
The platform sample is generated by the same backend/R2 atlas pipeline as real characters, but it is not a user character and it does not expose user id, layer config, or personal data. If the endpoint is unavailable, the SDK uses a tiny generated atlas as a resilience fallback unless disabled.
For a tiny browser test game, build the SDK and serve this package directory:
npm run build --workspace @oasiz/sdk
python3 -m http.server 4175 --directory packages/sdkThen open http://localhost:4175/examples/atlas-game.html. The example uses
getPlayerCharacter({ localFallback: true }), draws the returned atlas on a
canvas, and falls back to the bundled SVG sample if the atlas image cannot load.
const character = await oasiz.getPlayerCharacter();
// In Oasiz app: the real signed-in player's character.
// On localhost without the app bridge: "Oasiz Sample Jibble".
// On non-local pages without the app bridge: null.Use the options when you need explicit behavior:
// Keep the old "missing bridge means null" behavior.
const characterOrNull = await oasiz.getPlayerCharacter({ localFallback: false });
// Force the sample atlas in a browser test harness.
const sampleCharacter = await oasiz.getPlayerCharacter({ localFallback: true });
// Strict mode: require the platform-generated sample, with no generated backup.
const platformOnlySample = await oasiz.getPlayerCharacter({
localFallback: true,
generatedFallback: false,
});
// Override the sample endpoint when testing against staging.
const stagingSample = await oasiz.getPlayerCharacter({
localFallback: true,
sampleCharacterUrl: "https://staging-api.oasiz.ai/api/sdk/sample-character",
});HTML5 / TypeScript
import {
JIBBLE_ANIMATION,
JIBBLE_ANIMATION_IDS,
getJibbleAnimationId,
oasiz,
} from "@oasiz/sdk";
const character = await oasiz.getPlayerCharacter();
if (!character) return;
const atlas = character.textureAtlas;
// Print the full SDK catalog while debugging.
console.table(JIBBLE_ANIMATION_IDS);
const walkBack = getJibbleAnimationId("walk", "back"); // "walk_n"
const walkBackAnimation = atlas.animations.find((anim) => anim.animationId === walkBack);
const idleFrontAnimation = atlas.animations.find(
(anim) => anim.animationId === JIBBLE_ANIMATION.Idle.South,
);
const backflipAnimation = atlas.animations.find(
(anim) => anim.animationId === JIBBLE_ANIMATION.Backflip,
);
const availableAnimations = new Set(atlas.animations.map((anim) => anim.animationId));
const animationToPlay = availableAnimations.has(walkBack)
? walkBack
: JIBBLE_ANIMATION.Idle.South;Not every custom atlas is guaranteed to contain every catalog ID. Check
atlas.animations before playing an animation and choose a fallback, usually
idle_s or the closest available direction.
Unity
using Oasiz;
var walkBackId = JibbleAnimations.GetId(
JibbleAnimationAction.Walk,
JibbleDirection.Back); // "walk_n"
var walkFrontRightId = JibbleAnimations.GetId(
JibbleAnimationAction.Walk,
JibbleDirection.FrontRight); // "walk_se"
var idleFrontId = JibbleAnimations.GetId(
JibbleAnimationAction.Idle,
JibbleDirection.Front); // "idle_s"
var backflipId = JibbleAnimations.Backflip; // "backflip"
foreach (var animationId in JibbleAnimations.All)
{
Debug.Log(animationId);
}Platform bots
Use oasiz.requestBots() when a game needs platform-managed opponents or NPCs
with names, difficulty, characteristics, behavior, personality, and optional
Jibble atlas appearance.
The SDK forwards the request to the Oasiz host bridge
window.__oasizRequestBots(options). The host resolves that through the
platform bot pool for the current game; unsupported local hosts return null
instead of crashing.
HTML5 / TypeScript
import { oasiz } from "@oasiz/sdk";
const roster = await oasiz.requestBots({
count: 4,
difficulty: ["easy", "medium"],
poolKey: "default",
seed: "match-42",
includeAppearance: true,
});
if (!roster) {
// Bridge unavailable or backend rejected the request.
return;
}
for (const bot of roster.bots) {
console.log(bot.name, bot.difficulty, bot.characteristic);
console.log(bot.behavior, bot.personality);
const atlas = bot.appearance?.textureAtlas;
if (atlas) {
const idle = atlas.animations.find((anim) => anim.animationId === "idle_s");
console.log(idle?.frames);
}
}Options:
| Option | Type | Notes |
| --- | --- | --- |
| count | number | Positive integer. Omit to use the platform default. |
| difficulty | "easy" \| "medium" \| "hard" \| Array<...> | Filter by one or more difficulties. |
| poolKey | string | Optional game-configured bot pool key, such as "default" or "ranked". |
| seed | string | Optional deterministic selection seed for repeatable rosters. |
| includeAppearance | boolean | When true, bots can include a Jibble textureAtlas and editorTextureAtlas. |
Result fields:
| Field | Meaning |
| --- | --- |
| gameId, playerId, poolKey | Context used by the platform selection. |
| source | "game_pool", "platform_catalog", or "platform_catalog_fallback". |
| requestedCount, returnedCount | Requested and returned roster counts. |
| bots[] | Each bot has id, name, characteristic, difficulty, behavior, personality, and optional appearance. |
| bots[].appearance.textureAtlas | Same atlas shape as getPlayerCharacter().textureAtlas, so the Jibble animation catalog above applies. |
Unity
using Oasiz;
BotRequestResult roster = await OasizSDK.RequestBots(new BotRequestOptions
{
Count = 4,
Difficulties = new[] { BotDifficulty.Easy, BotDifficulty.Medium },
PoolKey = "default",
Seed = "match-42",
IncludeAppearance = true,
});
if (roster == null) return;
foreach (var bot in roster.bots)
{
Debug.Log($"{bot.name} ({bot.difficulty}): {bot.characteristic}");
Debug.Log(bot.behaviorJson);
Debug.Log(bot.personalityJson);
TextureAtlas atlas = bot.appearance?.textureAtlas;
if (atlas != null)
{
Debug.Log(atlas.imageUrl);
}
}Unity exposes behaviorJson, personalityJson, layerConfigJson, and
renderLayersJson as strings because Unity's built-in JsonUtility cannot
deserialize arbitrary nested JSON objects.
Score
oasiz.submitScore(score: number)
Submit the player's final score at game over. Call this exactly once per session, when the game ends. The platform handles leaderboard persistence — do not track high scores locally.
private onGameOver(): void {
oasiz.submitScore(Math.floor(this.score));
}scoremust be a non-negative integer. Floats are floored automatically.- Do not call on intermediate scores or level completions, only on final game over.
Haptics
oasiz.triggerHaptic(type: HapticType)
Trigger native haptic feedback. Always guard with the user's haptics setting.
type HapticType = "light" | "medium" | "heavy" | "success" | "error";| Type | When to use |
| --- | --- |
| "light" | UI button taps, menu navigation, D-pad press |
| "medium" | Collecting items, standard collisions, scoring |
| "heavy" | Explosions, major impacts, screen shake |
| "success" | Level complete, new high score, achievement unlocked |
| "error" | Damage taken, game over, invalid action |
// UI buttons — always light
button.addEventListener("click", () => {
oasiz.triggerHaptic("light");
});
// Tiered hit feedback
private onBallHit(zone: "center" | "edge"): void {
if (this.settings.haptics) {
oasiz.triggerHaptic(zone === "center" ? "success" : "medium");
}
}
// Game over
private onGameOver(): void {
oasiz.submitScore(this.score);
if (this.settings.haptics) {
oasiz.triggerHaptic("error");
}
}Haptics are throttled internally (50ms cooldown) to prevent spam.
Debugging
oasiz.enableLogOverlay(options?: LogOverlayOptions)
Mount an opt-in in-game console viewer for local debugging, QA sessions, or creator support. It mirrors console.log, console.info, console.warn, console.error, and console.debug into a floating overlay inside the game iframe. The overlay can be collapsed, repositioned by dragging the top bar, and resized from the bottom-right corner while the action buttons remain clickable.
const logOverlay = oasiz.enableLogOverlay({
enabled: new URLSearchParams(window.location.search).has("oasizLogs"),
collapsed: true,
});
console.log("[Boot] Scene ready");
// Optional cleanup if your game tears down and remounts
logOverlay.destroy();Options:
enabled: defaults totrue. Pass your own flag or query-param check here.collapsed: start with only the toggle pill visible.maxEntries: cap retained log lines. Defaults to200.title: optional label shown at the top of the panel. Defaults toSDK Logs.
The returned handle supports show(), hide(), clear(), isVisible(), and destroy().
Game state persistence
Persist cross-session data such as unlocked levels, inventory, or lifetime stats. State is stored per-user per-game in the Oasiz backend — available across devices and app reinstalls.
oasiz.loadGameState(): Record<string, unknown>
Returns the player's saved state synchronously. Returns {} if no state has been saved yet. Call once at the start of the game.
private initFromSavedState(): void {
const state = oasiz.loadGameState();
this.level = typeof state.level === "number" ? state.level : 1;
this.lifetimeHits = typeof state.lifetimeHits === "number" ? state.lifetimeHits : 0;
this.unlockedSkins = Array.isArray(state.unlockedSkins) ? state.unlockedSkins : [];
}Always validate the shape of loaded data — it may be {} on first play.
oasiz.saveGameState(state: Record<string, unknown>)
Queues a debounced save. Saves are batched automatically — call freely at checkpoints without worrying about request spam.
// Save after each level completion
private onLevelComplete(): void {
this.level += 1;
oasiz.saveGameState({
level: this.level,
lifetimeHits: this.lifetimeHits,
unlockedSkins: this.unlockedSkins,
});
}Rules:
- State must be a plain JSON object (not an array or primitive).
- Do not use
localStoragefor cross-session progress — usesaveGameStateso data syncs across platforms. - Do not store scores here — scores are submitted via
submitScore.
oasiz.flushGameState()
Forces an immediate write, bypassing the debounce. Use at important checkpoints like game over or before the page unloads.
private onGameOver(): void {
oasiz.saveGameState({ level: this.level, lifetimeHits: this.lifetimeHits });
oasiz.flushGameState(); // ensure it lands before the page closes
oasiz.submitScore(this.score);
}Layout
Use runtime viewport insets instead of hardcoded offsets. The SDK can read top, right, bottom, and left insets from host bridge values, per-side globals, or CSS safe-area environment values.
oasiz.getViewportInsets(): ViewportInsets
Returns both CSS pixels and percentages for every side:
const insets = oasiz.getViewportInsets();
hud.style.paddingTop = `${insets.pixels.top}px`;
hud.style.paddingRight = `${insets.pixels.right}px`;
hud.style.paddingBottom = `${insets.pixels.bottom}px`;
hud.style.paddingLeft = `${insets.pixels.left}px`;Percentages use the matching viewport axis: top / bottom are percentages of viewport height, while left / right are percentages of viewport width.
Hosts may expose pixels via window.getViewportInsets() or window.__OASIZ_VIEWPORT_INSETS__, for example { top, right, bottom, left } or { pixels: { top, right, bottom, left } }. Hosts may expose percentages via window.getViewportInsetsPercent(), window.__OASIZ_VIEWPORT_INSETS_PERCENT__, or a percent object. Per-side globals such as window.__OASIZ_SAFE_AREA_BOTTOM__ are also supported.
oasiz.getSafeAreaTop(): number
Legacy alias for oasiz.getViewportInsets().percent.top. Returns the top inset as a percentage of viewport height (0–100). To get pixels in JavaScript, prefer oasiz.getViewportInsets().pixels.top. In CSS, the percent value matches vh units (for example 12.5vh for 12.5% of the viewport height). Unsupported hosts return 0.
const safeTopPct = oasiz.getSafeAreaTop();
document.documentElement.style.setProperty("--safe-top", `${safeTopPct}vh`);oasiz.safeAreaTop
Getter alias for getSafeAreaTop().
oasiz.viewportInsets
Getter alias for getViewportInsets().
Recommended CSS pattern:
:root {
--safe-top: 0px;
}
#top-bar {
padding-top: var(--safe-top);
}oasiz.setLeaderboardVisible(visible: boolean): void
Show or hide the host leaderboard UI from inside the game. This only affects the leaderboard; back and social controls remain visible. Calls window.__oasizSetLeaderboardVisible when present.
function openCustomOverlay(): void {
oasiz.setLeaderboardVisible(false);
}
function closeCustomOverlay(): void {
oasiz.setLeaderboardVisible(true);
}Unsupported hosts safely no-op.
Graphics performance
oasiz.getGraphicsPerformance(): GraphicsPerformanceMetric
Returns a recommended FPS target and suggested rendering tier:
const graphics = oasiz.getGraphicsPerformance();
// { fps: number, tier: "minimal" | "low" | "medium" | "high" }
gameLoop.setTargetFps(graphics.fps);Hosts can inject measured values with window.getGraphicsPerformance() or window.__OASIZ_GRAPHICS_PERFORMANCE__; otherwise the SDK estimates from browser, device, and WebGL capability signals.
oasiz.graphicsPerformance
Getter alias for getGraphicsPerformance().
Lifecycle
The platform dispatches lifecycle events when the app goes to the background or returns to the foreground. Subscribe to pause game loops and audio accordingly.
oasiz.onPause(callback: () => void): Unsubscribe
oasiz.onResume(callback: () => void): Unsubscribe
Both return an unsubscribe function.
const offPause = oasiz.onPause(() => {
this.gameLoop.stop();
this.bgMusic.pause();
});
const offResume = oasiz.onResume(() => {
this.gameLoop.start();
this.bgMusic.play();
});
// Clean up when the game is destroyed
offPause();
offResume();Navigation
Use navigation hooks when your game needs to control back behavior (Android back / web Escape) or participate in host-driven close events.
oasiz.onBackButton(callback: () => void): Unsubscribe
Registers a callback for platform back actions. While at least one back listener is subscribed, back actions are routed to your game instead of immediately closing it.
Use this for pause menus, in-game overlays, or custom back-stack behavior.
If your callback throws, the SDK calls leaveGame() (host close) and rethrows the error so you still see it in devtools or error reporting. Non-Error throws are normalized to an Error (strings become the message; otherwise "Back button callback failed.").
const offBack = oasiz.onBackButton(() => {
if (this.isPauseMenuOpen) {
this.closePauseMenu();
return;
}
this.openPauseMenu();
});
// Restore default host back behavior when no longer needed
offBack();oasiz.enableBackButtonTesting(options?: BackButtonTestingOptions): BackButtonTestingHandle
Installs a local development bridge for browser testing. When a back handler is active, Escape and browser Back can dispatch the same oasiz:back event that the app sends. The returned handle can also trigger back or leave events manually.
if (import.meta.env.DEV) {
const backTesting = oasiz.enableBackButtonTesting();
debugBackButton.onclick = () => backTesting.triggerBack();
}oasiz.leaveGame(): void
Programmatically request the host to close the current game (for example, from a Quit button inside your game UI).
quitButton.addEventListener("click", () => {
oasiz.leaveGame();
});oasiz.share(options: { text?: string; score?: number; image?: string }): Promise<void>
Ask the host to open the same share flow Oasiz already uses today. Use text to customize the share message, score to trigger a challenge-style share, and image to share an http(s) URL or data:image/... payload.
await oasiz.share({
text: "I made it to level 9!",
score: 4200,
});oasiz.onLeaveGame(callback: () => void): Unsubscribe
Registers a callback fired when the host initiates closing the game (for example, close button, gesture, or host navigation). Use this for lightweight cleanup.
const offLeave = oasiz.onLeaveGame(() => {
oasiz.flushGameState();
this.bgMusic.pause();
});
// Clean up listener when destroyed
offLeave();Multiplayer
oasiz.shareRoomCode(code: string | null, options?: { inviteOverride?: boolean })
Notify the platform of the active multiplayer room so friends can join via the invite system. Pass null when leaving a room.
Set inviteOverride: true when your game wants to hide the platform invite pill and render its own invite button/UI. The platform still tracks the room code, but your game owns the invite entry point.
import { insertCoin, getRoomCode } from "playroomkit";
import { oasiz } from "@oasiz/sdk";
await insertCoin({ skipLobby: true });
oasiz.shareRoomCode(getRoomCode());
// On disconnect
oasiz.shareRoomCode(null);// Game-owned invite UI: hide the platform pill, keep room tracking
oasiz.shareRoomCode(getRoomCode(), { inviteOverride: true });oasiz.openInviteModal(): void
Opens the platform invite-friends UI when the bridge is available. Typically used together with shareRoomCode (for example, your own invite button calls this).
import { openInviteModal, shareRoomCode } from "@oasiz/sdk";
shareRoomCode("ABCD", { inviteOverride: true });
inviteButton.addEventListener("click", () => {
openInviteModal();
});Read-only injected values
These are populated by the platform before the game loads. Always check for undefined before using.
// The platform's internal game ID
const gameId = oasiz.gameId;
// Pre-filled room code for auto-joining a friend's session
if (oasiz.roomCode) {
await connectToRoom(oasiz.roomCode);
}
// Player identity for multiplayer games
const name = oasiz.playerName;
const avatar = oasiz.playerAvatar;Named exports
All methods are also available as named exports if you prefer not to use the oasiz namespace object:
import {
requestBots,
getPlayerCharacter,
submitScore,
share,
triggerHaptic,
loadGameState,
saveGameState,
flushGameState,
shareRoomCode,
openInviteModal,
getLaunchContext,
getLocalLaunchPlayer,
isLaunchHost,
enableAppSimulator,
enableLogOverlay,
enableBackButtonTesting,
getGraphicsPerformance,
getSafeAreaTop,
getViewportInsets,
setLeaderboardVisible,
onPause,
onResume,
onBackButton,
onLeaveGame,
leaveGame,
getGameId,
getRoomCode,
getPlayerId,
getPlayerName,
getPlayerAvatar,
} from "@oasiz/sdk";TypeScript types
import type {
AppSimulatorDevice,
AppSimulatorDeviceName,
AppSimulatorHandle,
AppSimulatorOptions,
AppSimulatorOrientation,
BackButtonTestingHandle,
BackButtonTestingOptions,
BotRequestOptions,
BotRequestResult,
GameState,
GraphicsPerformanceMetric,
GraphicsPerformanceTier,
HapticType,
LogOverlayEntry,
LogOverlayHandle,
LogOverlayLevel,
LogOverlayOptions,
ShareRequest,
ShareRoomCodeOptions,
PlatformBotProfile,
PlatformLobbyDefinition,
PlatformLobbyLaunchContext,
PlatformLobbyLaunchPlayer,
PlatformLobbyModeDefinition,
Unsubscribe,
ViewportInsetEdges,
ViewportInsets,
ViewportInsetSide,
} from "@oasiz/sdk";Unity WebGL SDK
C# API and WebGL-only OasizBridge.jslib live in this repository at packages/OasizSDK/. Copy the OasizSDK folder into your Unity project under Assets/ (for example Assets/OasizSDK).
Setup
- Copy
packages/OasizSDKfrom this repo intoAssets/OasizSDK. - Ensure the WebGL platform is selected for release builds; the
.jslibunderRuntime/Plugins/WebGL/is included automatically for WebGL. - Add an
OasizSDKcomponent to a persistent GameObject early (for example a bootstrap scene), or rely onOasizSDK.Instancewhich creates aDontDestroyOnLoadobject. The component registers listeners foroasiz:pause,oasiz:resume,oasiz:back, andoasiz:leaveviaSendMessage.
Quick start
using Oasiz;
using UnityEngine;
public class GameManager : MonoBehaviour
{
void Start()
{
// Ensure the singleton is initialized early
_ = OasizSDK.Instance;
// Subscribe to lifecycle events
OasizSDK.OnPause += OnPause;
OasizSDK.OnResume += OnResume;
// Offset UI for host chrome and browser safe areas
ViewportInsets insets = OasizSDK.GetViewportInsets();
float safeTopPx = insets.Top / 100f * Screen.height;
float safeBottomPx = insets.Bottom / 100f * Screen.height;
float safeLeftPx = insets.Left / 100f * Screen.width;
float safeRightPx = insets.Right / 100f * Screen.width;
Debug.Log($"Viewport insets: {safeTopPx}px top, {safeRightPx}px right, {safeBottomPx}px bottom, {safeLeftPx}px left");
// Tune visual detail for the current browser/device
GraphicsPerformanceMetric graphics = OasizSDK.GetGraphicsPerformance();
Application.targetFrameRate = graphics.fps;
Debug.Log($"Graphics profile: {graphics.tier} at {graphics.fps}fps");
PlatformLobbyLaunchContext launch = OasizSDK.GetLaunchContext();
if (launch != null)
{
Debug.Log($"Lobby room {launch.roomCode}, mode {launch.modeId}, settings {launch.settingsJson}");
}
// Emit score normalization anchors
OasizSDK.EmitScoreConfig(new ScoreConfig(
new ScoreAnchor(10, 100),
new ScoreAnchor(30, 300),
new ScoreAnchor(75, 600),
new ScoreAnchor(200, 950)
));
}
void OnGameOver(int finalScore)
{
OasizSDK.SubmitScore(finalScore);
OasizSDK.FlushGameState();
OasizSDK.SetLeaderboardVisible(true);
}
void OnGameplayStart()
{
OasizSDK.SetLeaderboardVisible(false);
}
void OnPause() => Time.timeScale = 0f;
void OnResume() => Time.timeScale = 1f;
void OnDestroy()
{
OasizSDK.OnPause -= OnPause;
OasizSDK.OnResume -= OnResume;
}
}API parity (TypeScript → C#)
| HTML5 (@oasiz/sdk) | Unity (Oasiz namespace) |
| --- | --- |
| oasiz.submitScore(n) | OasizSDK.SubmitScore(int) |
| oasiz.triggerHaptic(type) | OasizSDK.TriggerHaptic(HapticType) |
| oasiz.loadGameState() | OasizSDK.LoadGameState() → Dictionary<string, object> |
| oasiz.saveGameState(obj) | OasizSDK.SaveGameState(Dictionary<string, object>) |
| oasiz.flushGameState() | OasizSDK.FlushGameState() |
| oasiz.getViewportInsets() / viewportInsets | OasizSDK.GetViewportInsets() / OasizSDK.ViewportInsets (ViewportInsets, 0–100, top/bottom % of height and left/right % of width) |
| oasiz.getSafeAreaTop() / safeAreaTop | OasizSDK.GetSafeAreaTop() / OasizSDK.SafeAreaTop (float, 0–100, % of viewport height) |
| oasiz.getGraphicsPerformance() / graphicsPerformance | OasizSDK.GetGraphicsPerformance() / OasizSDK.GraphicsPerformance (GraphicsPerformanceMetric) |
| oasiz.setLeaderboardVisible(v) | OasizSDK.SetLeaderboardVisible(bool) |
| oasiz.onPause / onResume | OasizSDK.OnPause / OnResume static events |
| oasiz.onBackButton | OasizSDK.OnBackButton or SubscribeBackButton(Action) (reference-counts __oasizSetBackOverride) |
| oasiz.enableBackButtonTesting(options) | OasizSDK.EnableBackButtonTesting(bool keyboard = true, bool browserHistory = true, bool log = false) |
| oasiz.onLeaveGame | OasizSDK.OnLeaveGame |
| oasiz.leaveGame() | OasizSDK.LeaveGame() |
| oasiz.share(request) | OasizSDK.Share(ShareRequest) |
| oasiz.shareRoomCode | OasizSDK.ShareRoomCode(string, ShareRoomCodeOptions) |
| oasiz.openInviteModal() | OasizSDK.OpenInviteModal() |
| oasiz.getLaunchContext() / launchContext | OasizSDK.GetLaunchContext() / OasizSDK.LaunchContext |
| oasiz.getLocalLaunchPlayer() / localLaunchPlayer | OasizSDK.LocalLaunchPlayer |
| oasiz.isLaunchHost() / isHost | OasizSDK.IsHost |
| oasiz.getPlayerCharacter() | OasizSDK.GetPlayerCharacter() |
| oasiz.requestBots(options) | OasizSDK.RequestBots(BotRequestOptions) |
| oasiz.gameId / roomCode / ... | OasizSDK.GameId / RoomCode / PlayerId / PlayerName / PlayerAvatar |
| -- | OasizSDK.EmitScoreConfig(ScoreConfig) → window.emitScoreConfig (Unity-only helper for normalized score UI) |
| oasiz.enableLogOverlay | OasizSDK.EnableLogOverlay(LogOverlayOptions) (see note below) |
| -- | OasizSDK.AppendLogOverlay(level, message, stackTrace) (see note below) |
Share (Unity)
HTML5 oasiz.share returns a Promise you can await. Unity OasizSDK.Share(ShareRequest) returns void: C# validation throws ArgumentException with the same rules as TypeScript (at least one of text, score, or image; non-negative integer score; http(s) or data:image/...;base64,... image). The call forwards JSON to window.__oasizShareRequest. If the host promise rejects, the WebGL .jslib logs the error to the browser console.
OasizSDK.Share(new ShareRequest
{
Text = "Beat this run!",
Score = 1200,
Image = "https://example.com/card.png",
});Types
// Haptic feedback intensity
public enum HapticType { Light, Medium, Heavy, Success, Error }
// Host chrome and browser safe-area insets
public struct ViewportInsets
{
public float Top;
public float Right;
public float Bottom;
public float Left;
}
// Device graphics/rendering recommendation
public enum GraphicsPerformanceTier { Minimal, Low, Medium, High }
public struct GraphicsPerformanceMetric
{
public int fps;
public string tier;
public GraphicsPerformanceTier Tier { get; }
}
// Score normalization (exactly 4 anchors required)
public struct ScoreAnchor { public int raw; public int normalized; }
public struct ScoreConfig { public ScoreAnchor[] anchors; }
// Host share sheet (text / score / image URL or data URL)
public class ShareRequest
{
public string Text { get; set; }
public int? Score { get; set; }
public string Image { get; set; }
}
// Multiplayer invite options
public class ShareRoomCodeOptions { public bool InviteOverride { get; set; } }
// Platform lobby launch context
public class PlatformLobbyLaunchContext
{
public string gameId;
public string localPlayerId;
public string modeId;
public PlatformLobbyLaunchPlayer[] players;
public string roomCode;
public string settingsJson;
public string transportJson;
}
public class PlatformLobbyLaunchPlayer
{
public string displayName;
public bool isBot;
public int slotIndex;
public string userId;
}
// Platform bot roster
public enum BotDifficulty { Easy, Medium, Hard }
public class BotRequestOptions
{
public int Count { get; set; }
public BotDifficulty? Difficulty { get; set; }
public BotDifficulty[] Difficulties { get; set; }
public string PoolKey { get; set; }
public string Seed { get; set; }
public bool? IncludeAppearance { get; set; }
}
public class BotRequestResult
{
public string gameId;
public string playerId;
public string poolKey;
public string source;
public int requestedCount;
public int returnedCount;
public PlatformBotProfile[] bots;
}
public class PlatformBotProfile
{
public string id;
public string name;
public string characteristic;
public string difficulty;
public string behaviorJson;
public string personalityJson;
public PlatformBotAppearance appearance;
}
public class PlatformBotAppearance
{
public string kind;
public string characterName;
public string baseCharacterId;
public string compositionCode;
public string layerConfigJson;
public string renderLayersJson;
public TextureAtlas textureAtlas;
public TextureAtlas editorTextureAtlas;
}
// Log overlay configuration
public class LogOverlayOptions
{
public bool Enabled { get; set; } = true;
public bool Collapsed { get; set; } = false;
public int MaxEntries { get; set; } = 200;
public string Title { get; set; } = "SDK Logs";
}
// Log overlay lifecycle handle
public class LogOverlayHandle
{
public void Clear();
public void Hide();
public void Show();
public bool IsVisible();
public void Destroy();
}Back button and errors
Matching the HTML5 SDK: if any OnBackButton handler throws, OasizSDK.LeaveGame() is invoked and the original exception is rethrown (throw; preserves the stack trace). Use SubscribeBackButton when you want an unsubscribe delegate; you can also use OnBackButton += / -= directly.
// Enable Escape / browser Back simulation in local WebGL test builds
OasizSDK.EnableBackButtonTesting();
// Subscribe with automatic unsubscribe support
var offBack = OasizSDK.SubscribeBackButton(() =>
{
if (isPaused)
Resume();
else
Pause();
});
// Unsubscribe when no longer needed
offBack();Editor vs WebGL builds
In the Unity Editor, bridge calls are mostly logged and return safe defaults (for example viewport insets 0, null platform IDs, and an estimated graphics profile). Real host integration applies to WebGL player builds running inside Oasiz.
Log overlay (Unity)
The C# API for the log overlay exists for API compatibility, but the default OasizBridge.jslib in this repo does not inject DOM UI — EnableLogOverlay / AppendLogOverlay are no-ops at the JavaScript layer. Use Unity's console and device logs for debugging unless you replace or extend the .jslib on your side.
AppendLogOverlay(level, message, stackTrace) lets you pipe Debug.Log output into the overlay manually, since many embedded WebViews do not route Unity player logs through console.log. Valid levels: "debug", "log", "info", "warn", "error".
Local development
HTML5 / TypeScript
All methods safely no-op when the platform bridges are not injected. In development mode a console warning is logged so you know the call was made:
[oasiz/sdk] submitScore bridge is unavailable. This is expected in local development.No crashes, no special setup required for local dev.
Unity WebGL
The .jslib logs warnings when window.* bridges are missing (for example submitScore, __oasizLeaveGame). The Editor path avoids calling native plugins and prints Debug.Log for most operations instead.
