The Problem: One OS, Radically Different Hosts
OASIS_OS started as a PSP homebrew shell in 2006. Twenty years later, the same codebase runs inside a web browser, on a desktop with SDL3, embedded in Unreal Engine 5 as an in-game terminal, and as a kernel-mode overlay drawn directly into game framebuffers on the original PSP hardware.
These aren't five ports with shared libraries. It's one set of core crates — UI widgets, browser engine, terminal, skin system, window manager — compiled identically for each target. The only thing that changes is the backend: a thin implementation of rendering, input, audio, and networking traits.
The Backend Trait Boundary
The entire platform abstraction lives in a single file:
oasis-types/src/backend.rs. Every platform interaction in the system
passes through one of five traits:
Backend Trait Hierarchy
SdiCore (13 required methods — every backend must implement)
├── init, shutdown
├── clear, swap_buffers
├── fill_rect, blit, load_texture, destroy_texture
├── draw_text, measure_text
├── set_clip_rect, reset_clip_rect
└── read_pixels
SdiBackend (39 optional accelerated primitives)
├── SdiShapes rounded_rect, circle, line, triangle, polygon
├── SdiGradients linear_gradient, radial_gradient
├── SdiAlpha set_global_alpha, fill_rect_alpha
├── SdiText draw_text_styled, measure_text_styled
├── SdiTextures blit_rotated, blit_tinted, blit_region
├── SdiClipTransform push_clip, pop_clip, set_transform
├── SdiVector begin_path, move_to, line_to, arc, bezier, fill, stroke
└── SdiBatch begin_batch, end_batch
InputBackend poll() → Vec<InputEvent>
NetworkBackend TCP connect, read, write
AudioBackend play, pause, set_volume, load_audio
SdiCore is the minimum viable backend: 13 methods that let you clear a screen, draw rectangles, blit textures, render text, and read pixels back. Every widget, every app, the browser engine, the terminal — they all target this interface exclusively.
SdiBackend extends SdiCore with 39 optional accelerated primitives
split across 8 focused extension traits. Each has a default no-op or software fallback
implementation. If a backend supports hardware-accelerated gradients, it implements
SdiGradients. If not, the core rendering path handles it. This means a
new backend can ship with just 13 methods and progressively add hardware acceleration.
The Five Backends
Deployment Topology
┌──────────────────────────────────┐
│ oasis-core │
│ 16 apps · browser · terminal │
│ skin engine · window manager │
│ agent · plugins · scripting │
└──────────┬───────────────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌────────▼────────┐ ┌───────▼────────┐ ┌────────▼────────┐
│ backend-sdl │ │ backend-wasm │ │ backend-ue5 │
│ SDL3 + OpenGL │ │ Canvas 2D │ │ Software RGBA │
│ Linux/Mac/Win/Pi│ │ Web Audio │ │ Unreal Engine 5 │
└─────────────────┘ │ DOM Input │ └─────────────────┘
└────────────────┘
┌──────────────────┐ ┌───────────────────────┐
│ backend-psp │ │ plugin-psp │
│ sceGu + VRAM │ │ Kernel PRX overlay │
│ Full OS shell │ │ Hooks sceDisplaySet │
│ TLS 1.3 + Stream │ │ Draws into game FB │
└──────────────────┘ └───────────────────────┘
SDL3 Desktop (oasis-backend-sdl)
The primary development backend. Uses SDL3's GPU-accelerated 2D renderer for all
SdiCore operations, with full SdiBackend extension coverage. Compiles SDL3 from source
via the build-from-source feature — no system SDL installation
required. Handles keyboard, mouse, and gamepad input; audio playback via SDL_mixer.
WebAssembly (oasis-backend-wasm)
Targets wasm32-unknown-unknown via wasm-bindgen. Renders to an HTML
Canvas 2D context, captures DOM keyboard/mouse events as InputEvents,
and uses Web Audio for sound. The live demo on GitHub Pages is this backend
— the full OS running in your browser.
PSP EBOOT (oasis-backend-psp)
Native PSP rendering via sceGu (the PSP's OpenGL-like 3D API) with
textures in VRAM. The most constrained backend: 32MB RAM, 333MHz single-core MIPS,
480×272 resolution. Yet it runs the full shell with 18 skins, the browser engine,
90+ terminal commands, TLS 1.3 networking, and streaming media playback.
Unreal Engine 5 (oasis-backend-ue5 + oasis-ffi)
A software RGBA framebuffer backend exposed through a C-ABI FFI layer. UE5 (or any
C-compatible host) calls oasis_tick() each frame, reads back the pixel
buffer via oasis_get_buffer(), and uploads it as a dynamic texture.
The OS runs as an in-game computer terminal, complete with working browser and shell.
PSP Kernel PRX (oasis-plugin-psp)
The most exotic backend: a <64KB kernel-mode module that runs alongside games.
Loaded via PLUGINS.TXT by custom firmware, it hooks
sceDisplaySetFrameBuf to draw overlay UI directly into the game's
framebuffer on every VSync. Claims a PSP audio channel for background MP3 playback.
No dependency on oasis-core — direct framebuffer rendering with its own
minimal widget set.
The Crate Dependency Graph
The workspace is organized as 20 crates with a strict dependency hierarchy. Foundation types flow downward; no crate reaches up to its consumers.
oasis-types (Color, Button, InputEvent, backend traits, error types)
├── oasis-vfs virtual file system: MemoryVfs, RealVfs, GameAssetVfs
├── oasis-platform platform service traits: Power, Time, USB, Network, OSK
├── oasis-sdi scene display interface: named objects, z-order
├── oasis-net TCP networking, PSK auth, remote terminal, FTP
├── oasis-audio audio manager, playlist, MP3 ID3 parsing
├── oasis-ui 32 widgets: Button, Card, TabBar, ListView, flex layout...
├── oasis-wm window manager: drag/resize, hit testing, decorations
├── oasis-skin TOML skin engine, 18 skins, theme derivation
├── oasis-terminal 90+ commands, shell features, variable expansion
├── oasis-browser HTML/CSS/Gemini: DOM, CSS cascade, layout, JS bindings
├── oasis-js QuickJS-NG runtime, console API, DOM manipulation
├── oasis-video MP4/H.264+AAC decode, streaming pipelines
├── oasis-vector scene graph, path ops, icons, frame animations
└── oasis-core 16 apps, dashboard, agent, plugin, scripting
├── oasis-backend-sdl → oasis-app (binary)
├── oasis-backend-wasm
├── oasis-backend-ue5 → oasis-ffi (cdylib)
├── oasis-backend-psp (excluded from workspace, nightly)
└── oasis-plugin-psp (excluded from workspace, kernel mode)
Why This Works: The Key Design Decisions
No #[cfg] in Core
Platform-specific code is confined entirely to backend crates. The core crates
— oasis-ui, oasis-browser, oasis-terminal,
etc. — compile identically for every target. They accept a
&mut dyn SdiCore and draw to it. This eliminates the conditional
compilation spaghetti that plagues most cross-platform Rust projects.
Progressive Capability
The split between SdiCore (required) and the 8 extension traits
(optional) means backends can ship minimal implementations and progressively add
hardware acceleration. The PSP backend implements SdiShapes using sceGu
hardware primitives but falls back to software for gradients. The SDL3 backend
implements everything. The WASM backend sits in between. Core code never cares.
Virtual File System
File operations go through oasis-vfs traits, not std::fs.
On desktop, RealVfs wraps the real filesystem. In UE5,
GameAssetVfs maps to Unreal's asset system with an overlay write layer.
On WASM, MemoryVfs provides a fully in-RAM filesystem. On PSP, it wraps
sceIo* syscalls. Same API, radically different storage backends.
Bitmap Font Independence
Every backend ships its own glyph table in font.rs. No FreeType, no
system font dependencies. oasis-types provides glyph_advance()
with variable per-character widths (3–8px). This means text rendering works
identically everywhere — including on the PSP where the system font API
(psp::font) supplements the bitmap glyphs with TrueType via a VRAM atlas.
What 20 Crates Buys You
The aggressive crate split isn't just organizational hygiene. It has concrete engineering benefits:
- Compile-time isolation — Changing a widget in
oasis-uidoesn't recompile the browser engine. On a full rebuild, crates compile in parallel across dependency layers. - Feature-gated heavyweights —
oasis-videohas three feature flags:h264(links openh264),no-std-demux(lightweight PSP-safe parser),video-decode(full desktop pipeline). The PSP builds with onlyno-std-demux, saving megabytes. - Independent testing —
cargo test -p oasis-browserruns browser layout tests without compiling SDL3 or any backend. - Clear ownership — Each crate has a single responsibility.
oasis-skinowns TOML parsing and theme derivation.oasis-wmowns window hit testing and decorations. No god objects.
The trait boundary isn't just an abstraction — it's a compilation firewall. Backend crates are leaf nodes in the dependency graph. They depend on core; core never depends on them. This means adding a sixth backend (framebuffer, Android, iOS) requires zero changes to any existing crate.
Numbers
| Metric | Count |
|---|---|
| Workspace crates | 34 |
| Backend implementations | 5 |
| UI widgets | 32 |
| Terminal commands | 90+ |
| Skins (external TOML + built-in) | 18 |
| Apps (12 extracted crates) | 16 |
| SdiCore required methods | 13 |
| SdiBackend extended methods | 39 |
| Backend trait files to implement | 6 (backend/ directory) |