npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oasiz/sdk

v1.8.9

Published

Typed SDK for Oasiz game platform bridge APIs.

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/sdk

The 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, and sessionId
  • hostUserId and localPlayerId
  • selected modeId
  • validated settings
  • frozen players roster with slot indexes, readiness, teams/roles, and bot metadata
  • transport config 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/sdk

Then 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));
}
  • score must 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 to true. Pass your own flag or query-param check here.
  • collapsed: start with only the toggle pill visible.
  • maxEntries: cap retained log lines. Defaults to 200.
  • title: optional label shown at the top of the panel. Defaults to SDK 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 localStorage for cross-session progress — use saveGameState so 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

  1. Copy packages/OasizSDK from this repo into Assets/OasizSDK.
  2. Ensure the WebGL platform is selected for release builds; the .jslib under Runtime/Plugins/WebGL/ is included automatically for WebGL.
  3. Add an OasizSDK component to a persistent GameObject early (for example a bootstrap scene), or rely on OasizSDK.Instance which creates a DontDestroyOnLoad object. The component registers listeners for oasiz:pause, oasiz:resume, oasiz:back, and oasiz:leave via SendMessage.

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.