OhMyGame
OhMyGame is the open-source studio for making games with AI. Design, build, playtest, and publish games in one app, with your own model keys.
An embeddable UI runtime for games. Write React Native, ship it in Godot, a C/C++ engine or the browser. This whole site is rendered by JSUI's WebAssembly build into one canvas.
GamesC++ · JavaScriptGitHub @orktesOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Loading comments.
Checking sign-in status.
Low overhead Game and embedded UI middleware/runtime
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Fast development of cross-platform, beautiful, responsive in-game UIs or standalone Apps with React.
JSUI answers all four: you write plain React Native code that LLMs and web developers already know, and a small embeddable runtime renders it on the GPU inside your game, the same way on every platform.
The same frame in Godot: Godot draws the 3D world, JSUI draws everything on the right (panel, nameplates, buttons). More engines in integrations.
Using a coding agent? Add the JSUI skill to your project with one command:
npx @orktes/jsui-cli skill install # -> .claude/skills/jsui/ (--global: ~/.claude/skills/)
import { View, Text } from 'react-native').include/jsui/jsui.h): feed it input
(mouse, keyboard, touch, gamepad), tick it, get pixels — or let it draw
straight into your OpenGL / GLES / WebGL framebuffer or texture with the
optional Skia Ganesh GPU backend (jsui_render_gl).jsui dev and edits hot-update in place (React Fast Refresh keeps component state), with an error overlay for build and runtime errors.jsui dev. See docs/DEBUGGING.md.See docs/ARCHITECTURE.md for the design. For keeping per-frame work off JS (native animation, state styles, overlays, host-state binds), see docs/NATIVE_FIRST.md. For what JSUI costs a game (frame time, JS per frame, spikes, memory and size), see docs/PERFORMANCE.md.
Showcase: orktes.github.io/jsui, a site
rendered entirely by the JSUI WebAssembly build into one canvas: interactive
game screens, input, motion, a live-code playground and embedding guides. Its
source is ordinary React Native in site/ (node site/scripts/build.mjs,
then node site/scripts/serve.mjs). The same bundle runs natively in the viewer:
jsui-viewer --titlebar-overlay 64 --state 'audio={"native":true}' site/dist/app/bundle.js
(the state stands in for the page: the UI owning the sound switch; the nav bar holds the
window buttons). Its trailer's <Video> plays
there too, with AVFoundation on macOS.
The game shares state with the UI by key; the UI asks the game with calls that return promises. Details: docs/ARCHITECTURE.md#game-data.
jsui_set_state(rt, "gold", "1250"); // any time, even before the bundle loads
jsui_set_state(rt, "inventory", "{\"items\":{\"potion\":{\"name\":\"Potion\",\"count\":3}}}");
jsui_patch_state(rt, "inventory", "{\"items\":{\"potion\":{\"count\":2}}}"); // RFC 7386 merge patch
cfg.on_call = on_call; // Host.call() from JS: answer now or on a later frame
static void on_call(void* user, uint64_t id, const char* name, const char* args) {
if (strcmp(name, "shop.buy") == 0) jsui_resolve_call(rt, id, "{\"gold\":1100}");
else jsui_reject_call(rt, id, "unknown call");
}
import { Host, useHostState } from '@orktes/jsui-react';
function Inventory() {
const gold = useHostState('gold', 0); // re-renders only when "gold" changes
const { items } = useHostState('inventory', { items: {} });
const buy = () => Host.call('shop.buy', { item: 'potion' }).catch((e) => console.warn(e.message));
return (
<View>
<Text>{gold} gold</Text>
{Object.entries(items).map(([id, it]) => <Text key={id}>{it.name} ×{it.count}</Text>)}
<Button title="Buy potion" onPress={buy} />
</View>
);
}
State lives in the runtime, so it survives hot reloads. The UI can own keys
too (HostState.set → JsuiConfig.on_state), and the game can call handlers
the UI registers with Host.handle(name, fn) (jsui_call_ui).
Data that changes every frame (enemy positions for nameplates and markers)
skips JSON: the game pushes typed arrays, the UI reads them in place, and
Animated views can follow them natively without any JS per frame:
float enemies[30 * 3]; // x, y, hp per enemy
jsui_set_buffer(rt, "enemies", JSUI_BUFFER_F32, enemies, 30 * 3); // every frame
const count = useHostBufferSelector('enemies', (b) => b.length / 3, 0); // re-renders when the count changes
function Marker({ i }) {
const x = useHostBufferValue('enemies', i * 3), y = useHostBufferValue('enemies', i * 3 + 1);
return <Animated.View style={{ transform: [{ translateX: x }, { translateY: y }] }} />; // moved by the runtime
}
Pictures the game draws itself (a video it decodes, a render target, a camera) are shown by name; JSUI samples the engine's own GPU texture in place and repaints just those views when the game says it has new contents:
JsuiTexture t = {JSUI_TEXTURE_VULKAN, vk_image, 0, 1280, 720, JSUI_TEXTURE_OPAQUE};
jsui_set_texture(rt, "trailer", &t); // once (or when it changes)
jsui_texture_updated(rt, "trailer"); // after each new video frame
<HostTexture name="trailer" resizeMode="contain" style={{ width: 640, aspectRatio: 16 / 9, borderRadius: 12 }} />
Lottie animations play natively (Skia's Skottie, no JS per frame) with lottie-react-native's API:
import LottieView from 'lottie-react-native';
<LottieView source={require('./victory.json')} autoPlay loop style={{ width: 200, height: 200 }} />
Video with react-native-video's API: the host plays it (a <video> element in the
browser, AVFoundation in the macOS viewer, the engine's own player in a game) into a host
texture the view samples, so frames cost no JS. Hosts implement JsuiConfig.on_video:
import Video from 'react-native-video';
<Video source={{ uri: 'https://example.com/trailer.m3u8' }} paused={!playing} muted repeat
resizeMode="cover" style={{ width: 640, aspectRatio: 16 / 9 }} onEnd={next} />
Custom shaders: your own SkSL (Skia runtime effects) paint a view on every
renderer. Uniforms can be Animated values (also on the native driver), a
shader that reads time animates on the runtime clock, child shaders are
images or host textures, and uniform shader content post-processes the
view's own children:
<ShaderView
source={sksl`uniform float2 resolution; uniform float time; layout(color) uniform half4 tint;
half4 main(float2 p) { return tint * half(0.5 + 0.5 * sin(p.x / resolution.x * 6.28 + time)); }`}
uniforms={{ tint: animatedColor }}
style={{ width: 300, height: 120, borderRadius: 16 }}
/>
Native components: register your own view class from C, C++ or page JS that draws with Skia (a radar, a minimap, a gauge), straight from game state and without a React render, and use it like any React Native native component (docs/NATIVE_COMPONENTS.md):
struct Radar : jsui::Component {
void draw(jsui::Canvas& c, const JsuiDrawInfo& info) override {
for (const Unit& u : game.units()) c.circle(u.x, u.y, 3, jsui::Paint(0xffff4444));
}
};
jsui::registerComponent<Radar>(rt, "Radar", JSUI_COMPONENT_CLOCKED);
const Radar = requireNativeComponent('Radar');
<Radar range={120} onSelect={(e) => target(e.nativeEvent.index)} style={{ width: 200, height: 200 }} />
@orktes/jsui-react (the React Native API), @orktes/jsui-cli (jsui build / jsui dev,
headless jsui screenshot / inspect / run, and the agent skill) and @orktes/jsui-web
(the browser host with the WASM modules) are published to npm as 0.x releases. See
docs/RELEASING.md.
npx @orktes/jsui-cli skill install adds the JSUI agent skill
to a project (.claude/skills/jsui/; nothing else to install first), or install it in Claude Code as a plugin with
/plugin marketplace add orktes/jsui and /plugin install jsui@jsui. It teaches the
supported React Native subset, gamepad focus, animation, game data, Lottie, shaders, host
textures and embedding, and how to verify a UI without a browser (with
@orktes/jsui-cli in the project's devDependencies):
npx jsui screenshot app.jsx -o shot.png # the CPU WASM runtime in Node, deterministic
npx jsui inspect app.jsx # view tree: types, testIDs, text, focus, layout
npx jsui run app.jsx steps.json # scripted clicks / keys / gamepad / state + expectations
Every runtime that loads the bundle from jsui dev in dev mode (the game, the native
viewer, the browser at http://127.0.0.1:5173/shared, or jsui dev --headless inside the
server) joins its shared session: an agent drives and screenshots the live one with
jsui screenshot|inspect|run --attach http://127.0.0.1:5173 while you play it, and reads
what you did with jsui log --attach.
Every release on GitHub Releases has the C API
as a shared library (libjsui.so for Linux x64 / arm64, a universal libjsui.dylib,
jsui.dll) with jsui.h, a CMake package (find_package(jsui) → jsui::jsui) and the
viewer; JSUI.xcframework for iOS; and the Godot addon (addons/jsui, every desktop
platform in one zip). The library exports only the jsui_* functions, so it drops into
any engine or toolchain. See docs/RELEASING.md.
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/jsui-tests
./build/jsui-render bundle.js -o frame.png --width 1280 --height 720
./build/tools/jsui-viewer/jsui-viewer bundle.js # window, rendered on the GPU (--cpu: CPU raster, --gl: Ganesh on GL)
./build/jsui-record bundle.js --timeline examples/showcase/session.json --ffmpeg out.mp4 # scripted demo video
WebAssembly: GPU=1 scripts/build-wasm.sh (Skia m157 with Graphite on WebGPU, Ganesh on
WebGL2 and the CPU raster in one module, web/dist-gpu/: preferred when the browser has
WebGPU), WEBGL=1 scripts/build-wasm.sh (Skia on WebGL2, web/dist-webgl/) and
scripts/build-wasm.sh (CPU raster only, web/dist/: the fallback module, and what CI's
CPU browser tests use). web/jsui-web.js picks WebGPU, then WebGL, then the CPU raster.
GPU rendering is the default everywhere a GL / WebGL context is available; the CPU
raster is the fallback (jsui-viewer --cpu, createJsui({ renderer: 'cpu' })) and
what the deterministic tools (jsui-render, jsui-record) and reference tests use.
integrations/godot is a GDExtension with a JsuiView Control and a demo
project. Build it with -DJSUI_BUILD_GODOT=ON (it is off by default). See
integrations/godot/README.md.
integrations/ios is a UIKit app rendering with
Skia on Metal (RUN=1 scripts/build-ios.sh builds it and runs it in the
simulator). integrations/android is an NDK
NativeActivity app rendering with Skia on OpenGL ES (RUN=1 scripts/build-android.sh builds the APK and runs it on a device or emulator).
Both forward touch, hardware keys and gamepads and publish the safe area.
JSUI is a C library, so any language with a C FFI can host a React UI.
bindings/rust (a crate) and
bindings/go (a cgo package) are thin bindings over the
C API, and each comes with the same desktop app: a quest log whose data and
rules live in Rust or Go while the UI is React, drawn by Skia on the GPU into a
GLFW window. The program publishes shared state, the UI asks for changes with
Host.call, and the program answers. See bindings/README.md.
integrations/raylib, integrations/sdl3 and integrations/sokol run one
shared game HUD in three game loops through the C API: input pass-through
(what the UI does not consume moves the camera), per-frame unit positions
driving nameplates with no JS per frame, and three ways to get the UI on
screen (Skia GL into a raylib RenderTexture2D, Skia into SDL's own texture on
its Metal, Vulkan or GL renderer, Skia into an image on sokol_gfx's Metal
device on macOS and straight into sokol's GL swapchain framebuffer elsewhere).
Build them with -DJSUI_BUILD_RAYLIB=ON -DJSUI_BUILD_SDL3=ON -DJSUI_BUILD_SOKOL=ON. See integrations/README.md.
The same game also runs in Unity 6: integrations/unity
is a Unity package (a native plugin exporting the C API to C#, and a
JsuiView component that composites the UI as a uGUI overlay and passes on
the input it did not consume) and an example project built from the command
line. integrations/unreal is the same for
Unreal Engine 5: a plugin (UJsuiWidget / SJsuiView, drawing with Skia
Graphite on the Metal RHI's own queue) over JSUI as a shared library with just
the C API, and a C++ example project packaged headlessly with RunUAT.