Choose the renderer by content
| Content | Entity type | Native | WebAssembly |
|---|---|---|---|
| Shapes, text, controls | regular CANVAS_ENTITY_* |
Raylib | Raylib/WASM |
| Lucide icons | CANVAS_ENTITY_LUCIDE_GALLERY |
Raylib line segments | Raylib/WASM line segments |
| Image URL or file | CANVAS_ENTITY_IMAGE |
CEF off-screen pixels uploaded to a Raylib texture | positioned <img> overlay |
| Live web page | CANVAS_ENTITY_WEB_CONTENT |
CEF off-screen pixels uploaded to a Raylib texture | positioned <iframe> overlay |
Do not route vector icons or ordinary components through CEF. They belong in
Canvas_Draw_Entity() so they inherit camera transforms, theme colors,
selection, dragging, pinning, and context behavior without another rendering
runtime.
Raylib entities
Canvas_Draw_World() opens one BeginMode2D() camera and draws every retained
entity through Canvas_Draw_Entity(). Components such as buttons, dropdowns,
cards, calendars, tables, and switches are immediate Raylib draw calls backed
by state in Canvas_Entity.
The scene-array order is the z-order. The main frame interleaves each Raylib entity layer with its native CEF/image texture, so a later button can genuinely draw over and receive input ahead of an earlier browser surface. Picking and native browser input both resolve the same reverse scene order.
Dynamic text inside an entity must use the shared measured wrapping path rather
than a raw DrawTextEx() call. The layout splits both words and long unbroken
strings to the available width and draws only complete lines that fit the
component's content height. Cards, accordions, buttons, text areas,
notifications, conversations, search fields, and text chips therefore keep
database, agent, and user-provided text inside their bounds.
The Lucide gallery follows the same path. The generator
tools/generate_lucide_data.mjs downloads pinned Lucide 1.31.0 definitions,
flattens SVG paths and primitives into line segments, and writes
generated/lucide_data.h. The gallery virtualizes rows: only cards fully inside
its visible body issue Raylib draw calls. Its Raylib search field filters icon
names case-insensitively and resets the internal scroll offset when edited.
Regenerate after changing the pinned icon version:
node yuu/tools/generate_lucide_data.mjs
Native image and web surfaces
web_surface_native.cc maintains a pool of at most CANVAS_MAX_WEB_VIEWS
off-screen CEF browsers. Visible image and web entities are reconciled by stable
entity ID. CEF paint callbacks fill arena-owned RGBA buffers; Sync() creates or
updates Raylib textures; Draw() composites those textures into the
camera-derived screen bounds.
Resolution follows displayed size in stable buckets and is capped. Off-screen
entities release their active pool slot, small background views receive lower
frame rates, and the focused view receives the largest frame budget.
Native and WebAssembly backends consume the same tested
Canvas_Web_Surface_Should_Activate() camera policy: surfaces are inactive
outside the viewport, while being removed, or below 0.30x zoom. Native CEF
calls WasHidden(true) for inactive browsers; the web backend removes inactive
DOM surfaces at frame end.
When a web entity is selected, C reserves a fixed-screen-height toolbar above
the page viewport. The reserved height and toolbar clip animate together so
selection pushes the page down like an opening accordion and deselection pulls
it back up. Raylib draws the themed editable address field and Lucide
navigation icons after that entity's CEF texture, preserving scene z-order.
The URL editor retains its own draft, caret, selection, horizontal scroll, and
clipboard focus; committing updates the entity URL, which triggers backend
navigation. CEF reports main-frame address changes and history availability
back to the toolbar.
CEF emulates the canvas prefers-color-scheme value and updates the page
color-scheme when the canvas theme changes.
Images use a bundled image-view.html wrapper for object-fit: contain.
Filesystem paths are resolved to file://; URLs remain URLs.
WebAssembly image and web surfaces
web_surface_web.c cannot composite cross-origin page pixels into WebGL, so it
positions DOM overlays over the Raylib canvas. It creates <img> elements for
images and sandboxed <iframe> elements for live pages, converts Raylib screen
coordinates to CSS coordinates, and removes overlays that are no longer
visible.
Selected iframe entities use the same Raylib toolbar geometry and move the DOM
viewport below it. The iframe element receives the canvas color-scheme.
History navigation is best-effort because cross-origin iframe history and
address inspection remain browser-controlled; the displayed URL falls back to
the entity's configured URL.
This is intentionally different from native. Shared scene semantics live in C; the platform backend owns only presentation and input details.