↗ OpenLaunchBack to library
AGENT INTERFACE · VERSION 1

Play with intention.

OpenLaunch exposes a public, frame-based control interface in the game page. Import a game using the labeled file control and launch it before using the API. All game assets and saves remain local.

// Read real runtime status and frame counter.
OpenLaunch.getState()

// Read actual game-frame times and session errors without pausing.
OpenLaunch.getDiagnostics()

// Move for 30 game frames, then release.
await OpenLaunch.press('Up', { frames: 30 })

// Hold multiple buttons for a run or a menu command.
await OpenLaunch.press(['Right', 'B'], { frames: 60 })

// Tap DS touch coordinates, independent of screen layout.
await OpenLaunch.tap(128, 96, { frames: 2 })

// Camera changes are applied by the game thread.
OpenLaunch.camera({ yaw: 34, pitch: 8, zoom: 0.84 })

// Change 3D detail while preserving screen coverage and touch coordinates.
OpenLaunch.quality({ mode: 'smooth' }) // or 'sharp'
// Read renderQuality to distinguish requested from applied settings.
OpenLaunch.getDiagnostics()

// Optional scenery lighting; 0 disables it. Saved separately from quality.
OpenLaunch.highlights({ strength: 0.2 }) // range 0–0.35, default 0
// directionalHighlights reports requestedStrength, appliedStrength, fieldActive.
// Applying takes a render boundary; outside field scenes appliedStrength is 0.
OpenLaunch.getDiagnostics()

// Optional clear-day outdoor haze. Effective strength can be0 in other scenes.
OpenLaunch.haze({ strength: 0.08 }) // range0–0.12, default0
OpenLaunch.getDiagnostics().fieldHaze

// Debug A/B comparison: keep the same scene, camera and graphics quality.
// This affects only neighboring scenery and is never saved.
try {
  OpenLaunch.sceneryCulling({ enabled: false })
  await OpenLaunch.waitFrames(128)
  OpenLaunch.getDiagnostics() // scenery + gameFrameIntervals
} finally {
  OpenLaunch.sceneryCulling({ enabled: true }) // always restore the default
}

// Debug terrain packet reuse without changing the scene or persisting a setting.
try {
  OpenLaunch.terrainPackets({ enabled: false })
  await OpenLaunch.waitFrames(128)
  OpenLaunch.getDiagnostics() // terrainPackets + gameFrameIntervals
} finally {
  OpenLaunch.terrainPackets({ enabled: true }) // always restore the default
}

await OpenLaunch.waitFrames(30)
await OpenLaunch.step(['A'], {frames:2, settleFrames:2})
await OpenLaunch.step([], {frames:60, settleFrames:0}) // Wait, then pause visibly.
OpenLaunch.pause(true)
OpenLaunch.pause(false)
OpenLaunch.releaseAll()

Buttons: A, B, X, Y, Up, Down, Left, Right, L, R, Start and Select. Commands run sequentially. Pause, focus loss, launcher dialogs and explicit release cancel active and queued commands; integrations should handle the rejected promises and observe the current scene before retrying. Releasing input clears both engine and launcher holds. Button presses allow 1–600 frames; touch taps allow 1–120. A command times out and releases input if the game stops advancing.

Paused agent steps: step(buttons=[], {frames:2, settleFrames:2}) resumes only the current captured game, applies the optional pulse, waits, then pauses with the canvas clear. The toolbar shows “Paused for agent”; Resume returns to ordinary play. Returned state has paused:true and agentStepping:true. Empty buttons make a wait-only step. Frames must be at least1, settleFrames at least0, and their sum at most600. Pulse acknowledgement and browser scheduling can add frames; this is bounded input, not exact single-frame simulation. Invalid arguments do nothing. Focus loss, dialogs, explicit release/pause or game replacement cancel the step; their state is never overridden. An error while the step still owns the game releases input and pauses it. Ordinary press and normal player Pause keep their existing behavior.

Field characters: Diagnostics include fieldCharacters: loaded resources, current draw count and vertices, GPU bytes, errors and the last submitted pose. The pose reports graphics identity, facing, movement animation, clip and source frame; it may be stale when no character was drawn. New 3D scenery includes on-foot Lucas and Dawn on foot and bicycles, youngsters, lasses, middle-aged townspeople and both shop-staff types. Cycling, surfing and unsupported actions retain the original artwork.

Observe before acting. Use the visible game canvas to identify the current scene. The status API reports actual rendered frames, field presentation, pause state and launch errors; it does not invent game-state labels or expose a hidden emulator.

Browser automation: Controls have accessible names and stable IDs. The file inputs are #rom-file and #save-file; launch is #launch, game canvas is #canvas, and button controls have data-button="A", etc. Use the registered WebMCP tools for holds: openlaunch_state, openlaunch_diagnostics, openlaunch_press, openlaunch_step, openlaunch_wait, openlaunch_touch, openlaunch_camera, openlaunch_quality, openlaunch_highlights, openlaunch_haze, openlaunch_custom_scenery, openlaunch_scenery_culling, openlaunch_terrain_packets, openlaunch_gameplay_terrain_culling, openlaunch_pause and openlaunch_release. Browsers without WebMCP can use accessible on-screen controls. The JavaScript API is also available to integrations that permit page actions.

Measure movement: Frame timing covers up to120 completed game-frame intervals since the latest resume. Intentional pauses clear the timing window and omit the mixed pause/resume interval; actual long gameplay frames remain counted. Observe a clear route, move for at least 128 frames, then read diagnostics immediately. Compare average FPS, p95 and maximum frame times; an idle average does not establish smooth movement. Record the device, output dimensions and build with the result.

Scenery debugging: The optional scenery diagnostics object reports tested, culled, fallback and coldLoaded for the latest neighboring-scenery pass, not the 120-frame timing window. A read during a pass may briefly return zero counts; unavailable exports return null. Fallback includes terrain drawn without a supported bound and all neighboring terrain when culling is disabled. Cold loads are new chunks loaded during that pass. The debug toggle applies at the next scenery pass; its sceneryCullingRequested response acknowledges the request, not completion. Always restore enabled: true after comparisons.

Custom scenery preview: OpenLaunch.customScenery({enabled:true}) enables authored trees and forest rows in supported temperate, snowy and tropical areas, regional ground materials, and supported service buildings and homes; false restores original scenery. Unsupported source resources retain original art. The preference defaults off. Read customSceneryRenderer for independent requested, GPU-ready, mesh-loaded, placement-pack, matched-owner, last-frame instance/draw and error observations. Building diagnostics are in customPropRenderer: loaded mesh/atlas/identity counts, registered models, frame draws/indices and error. Ground diagnostics are in groundMaterials: material count, owned bytes, and cumulative matches/misses. Compare unsigned32 counter deltas; these fields are observed independently. The original/custom scenery toggle includes supported grass and path materials. Loaded art does not imply visible replacement in the current scene. All placements are derived locally from the imported backup. This preview is not a claim that the entire world has replacement art.

Battle environments: Custom scenery also enables authored meadow, forest, mountain, snow, water and three cave arenas. battleEnvironment reports loaded resources, active battle state, the last ordinary-layer mask, mesh uploads, cached scene/shadow renders, error, target width/height and background ID. Active state alone does not prove a replacement was drawn; inspect the mask and visible canvas. Ordinary warm frames reuse the scene texture. Orientation, quality and time-of-day changes may redraw it; switching background families uploads a different mesh/material. Attack-specific backgrounds and unsupported families retain original rendering. Pokémon, platforms, effects and combat remain game-owned. These counters are not GPU timings or a full-campaign guarantee.

Gameplay terrain and render work: The separate gameplayTerrain object reports the latest pass over up to four gameplay chunks: tested, culled, fallback and independently observed enable state. Use OpenLaunch.gameplayTerrainCulling({enabled:false}) only for comparisons and restore enabled:true in finally. Props retain independent visibility. renderWork is one coherent completed-render-frame snapshot of draw calls, vertices/upload bytes, deferred work, texture misses/upload/hash bytes and handled SDK commands. These are workload counts, not GPU timings or WebGL proxy calls; sequence identifies the sampled frame, and fields saturate at unsigned32 limits. Missing or unstable snapshots return null.

Terrain packet debugging: The optional terrainPackets diagnostic reports hits, misses and fallbacks as cumulative unsigned 32-bit counters. Compare deltas within the same running engine, using (after - before) >>> 0 to account for wraparound. bytes, entries and owners describe current cache usage. Fields are read independently and may span a renderer update; they are not one transactional snapshot. enabled is the engine flag observed during the read, not a requested value or proof that an already-running replay has finished. Missing or invalid exports return null. The debug toggle is never saved and defaults on. Always restore enabled: true in finally after comparisons.

Safety of progress: Use the in-game SAVE menu. The launcher snapshots a save only when the native game's save transaction finishes. Export current or previous saves through the player toolbar. Importing a save restarts the game; no API command silently overwrites an existing save.

Contact shading: Use openlaunch_contact with strength 0–0.5, or OpenLaunch.contact({strength}). It defaults off and saves independently of quality, highlights and haze. fieldContact reports requested/applied strength and observed status. Supported field views include interiors and nighttime; original fog and unsupported cameras bypass it. This is screen-space shading, not ray tracing.

Sun shadows: Use openlaunch_sun with strength 0–0.5, or OpenLaunch.sun({strength}). The option defaults off and is saved independently. fieldSun reports requested/applied strength and status. Clear daytime outdoors and a supported depth camera are required; original fog takes priority. Only visible geometry casts these screen-space shadows, so hidden scenery is missing and edges fade. This is not hardware ray tracing.

Battle commands: openlaunch_battle_menu returns game-owned commands or moves with names, types, PP, slots and a revision. Resume first, read a fresh menu with ready:true, then call openlaunch_battle_choice({revision,slot}). Read again after every choice; stale and duplicate requests are rejected. Pause, focus loss, dialogs and menu changes cancel pending choices. Paused diagnostic snapshots may retain labels but are not actionable. Bag, party, target and other menus use the original buttons or logical touch coordinates. No battle logic is reimplemented in the launcher.

Battle platforms: diagnostics.battlePlatforms exposes source sprite poses, visibility and the baked background handoff. Its renderer reports the eligible side mask, cached draws, uploads and errors. Rendering follows original terrain identities and palette fades in supported custom battle environments. This is observation, not a gameplay mutation API.

Controllers: Standard-mapped Gamepad API devices work through the same input bridge, including supported Bluetooth controllers paired in device settings. The right stick orbits and tilts the field camera; left / right triggers zoom out / in. Camera controls stop outside field scenes. Controller input stops while paused, unfocused or in a launcher dialog. Disconnecting a controller releases its input.

The browser build uses the decompiled game's C/C++ source compiled to WebAssembly with WebGL 2. It does not emulate the DS CPU. Network multiplayer is unavailable, and full-campaign and physical-iPhone validation remain pending.