Watch
2
0
Fork
You've already forked raylib-cs
0

chore: clean recommit

This commit is contained in:
tiger tiger tiger 2026-07-07 23:47:38 +02:00
commit 60ad2e7fb1
122 changed files with 23950 additions and 323 deletions

View file

@ -1,15 +1,11 @@
# Examples/Web — raylib-cs in the browser (WebAssembly)
A small browser app that runs raylib examples in the browser via WebAssembly, with a dropdown to
switch between them. It exists to **prove the `Raylib-cs` NuGet package's `browser-wasm` support
works end-to-end**: the package's `buildTransitive/Raylib-cs.targets` automatically links the
shipped `runtimes/browser-wasm/native/raylib.a` (and adds `-sUSE_GLFW=3`) into the .NET wasm
runtime. The browser host lives in `Examples/Web`, while `Examples.csproj` remains the single
examples project.
Runs the raylib examples in the browser via WebAssembly, with a dropdown to switch between them.
It proves the `Raylib-cs` NuGet package's `browser-wasm` support end-to-end: the package's
`buildTransitive` targets link the shipped `raylib.a` into the .NET wasm runtime.
The browser-wasm configuration is enabled only when publishing `Examples` with
`RuntimeIdentifier=browser-wasm`, so normal solution builds do not require the `wasm-tools`
workload.
The browser-wasm configuration only activates when publishing with
`RuntimeIdentifier=browser-wasm`; normal solution builds don't need the `wasm-tools` workload.
## Prerequisites
@ -27,23 +23,11 @@ dotnet publish Examples -f net10.0 -r browser-wasm -c Release
### Toolchain caveat
If the link step fails with:
```
wasm-opt: Unknown option '--enable-bulk-memory-opt'
```
your installed `wasm-tools` workload (Binaryen) is out of sync with the SDK — emscripten passes a
feature flag the bundled `wasm-opt` doesn't understand (commonly caused by a stale workload band
or a conflicting system EMSDK on PATH/`$EMSDK`). Proper fix:
```bash
dotnet workload update
```
`Examples.csproj` defaults browser-wasm publishes to unoptimized native linking so the standard
publish command works on affected local toolchains. A fully optimized publish can override those
MSBuild properties after updating the workload/toolchain.
If the link step fails with `wasm-opt: Unknown option '--enable-bulk-memory-opt'`, the installed
`wasm-tools` workload is out of sync with the SDK (stale workload band or a system EMSDK on
PATH); fix with `dotnet workload update`. As a safety net, `Examples.csproj` defaults
browser-wasm publishes to unoptimized native linking; an optimized publish can override those
properties.
## Run
@ -58,51 +42,40 @@ Open the printed URL and use the **Example** dropdown to switch examples.
## Canvas scaling modes
The browser host keeps raylib's internal render buffer fixed to `800x450` and applies display
scaling in CSS. Use the **Scale** dropdown (or `?scale=` query param) to choose behavior:
The render buffer stays fixed at `800x450`; display scaling is CSS-only. Pick with the **Scale**
dropdown (or `?scale=` query param):
- `native` (default): exact `800x450` CSS pixels (1x), no resizing.
- `integer`: largest whole-number multiple that fits the viewport, centered with letterboxing.
Best for pixel-perfect presentation.
- `fit`: fills available viewport while preserving aspect ratio (can be fractional, less crisp on
some DPI/zoom combinations).
- `native` (default): exact `800x450` CSS pixels.
- `integer`: largest whole-number multiple that fits, centered with letterboxing (pixel-perfect).
- `fit`: fills the viewport preserving aspect ratio (can be fractional, less crisp).
Scaling is computed in **device pixels first** and then converted back to CSS pixels using current
`devicePixelRatio`, which decouples the modes from needing a specific browser zoom level on
125%/150% OS scale displays.
`main.js` also shows runtime diagnostics in the status bar (`DPR`, CSS size, backing size, scale)
to help debug OS scaling / browser zoom behavior across monitors.
### Manual validation matrix (expected behavior)
- OS scale `100%`, browser zoom `100%`: `integer` should appear crisp and stable (`Scale 1.00` or
higher if viewport allows).
- OS scale `125%` and `150%`, browser zoom `100%`: `integer` should stay crisp (no subpixel CSS
scaling); perceived physical size changes with OS scale are expected.
- Browser zoom `80%`, `100%`, `125%`: `integer` should continue using whole-number scaling;
`fit` may show softening at non-integer effective scales.
- `native` mode should always report `CSS 800x450` and `Scale 1.00`.
- During window resize, there should be no frame-by-frame jitter because scale updates run on
resize/mode changes, not per animation frame.
Scaling is computed in device pixels and converted back to CSS via `devicePixelRatio`, so the
modes behave consistently across OS scale and browser zoom. The status bar shows the live numbers
(`DPR`, CSS size, backing size, scale): `integer` should stay crisp at any OS scale or zoom, and
`native` should always report `Scale 1.00`.
## How it works
A browser can't run raylib's blocking `while (!WindowShouldClose())` loop (it would freeze the
page), so frames are driven from JavaScript:
- `Host.Main()` calls `InitWindow` once and selects the default example (`main.js` runs it via
`runMain()`).
- `main.js` binds the page `<canvas>` to the runtime, then calls `Host.UpdateFrame()` every
`requestAnimationFrame` tick. `UpdateFrame`, `SetExample`, and `GetExampleNames` are `[JSExport]`.
- Each base example implements `IExample` (`Init` / `Update` / `Unload`) in `#if BROWSER` partials.
The host owns the window;
examples never call `InitWindow`/`CloseWindow`.
- `Host.Main()` calls `InitWindow` once; `main.js` then populates the dropdown and selects the
first example via `Host.SetExample`, so init failures surface in the on-page error banner.
- `main.js` binds the page `<canvas>` to the runtime and calls `Host.UpdateFrame()` from a
`requestAnimationFrame` loop paced to the current example's `TargetFps` (raylib's own limiter
busy-waits and would peg the main thread). The `Host` methods it calls are `[JSExport]`.
- On switch, `Host.SetExample` unloads the previous example, resets the cursor to visible, and
applies the next `TargetFps`. Cursor hiding and `ConfigFlags` are not honored in the browser.
If `Init` or `Update` throws, the example is unloaded and an error banner is shown.
- Each example is a single `.cs` file implementing `IExample` (`Init` / `Update` / `Unload`); the
host owns the window, so examples never call `InitWindow`/`CloseWindow`. Platform differences
(e.g. GLSL 100 vs 330) are handled inline with `#if BROWSER` guards, not separate files.
## Adding more examples
For a new desktop example (`../Core/...`, `../Shaders/...`, etc.), add a browser partial
(`*.Browser.cs`) that implements `IExample` and mirrors the split of its monolithic `Main`:
Examples are **auto-discovered by reflection** (`ExampleRegistry.DiscoverAll`) — no list to edit.
Drop a new `.cs` file in the matching category folder implementing `IExample`, splitting the
original monolithic `Main` as:
```
Main() { <setup>; while(!WindowShouldClose()){ <body> } <cleanup> }
@ -111,8 +84,8 @@ Main() { <setup>; while(!WindowShouldClose()){ <body> } <cleanup> }
Unload(){ <cleanup, minus CloseWindow> }
```
Then add `new YourExample()` to the `Examples` list in `Host.cs`.
Keep the standalone `static Main()` as a thin driver so the example still runs on its own.
Examples that load files (`resources/...`) also need those assets in the wasm virtual filesystem.
`Examples.csproj` bundles `../resources/` into the browser app when publishing with
`RuntimeIdentifier=browser-wasm`.
If an example can't run on single-threaded wasm, add its type to `DesktopExcludedFromBrowser` in
`ExampleRegistry.cs`; `BrowserOnly` is the inverse list. Assets under `resources/` are bundled
into the wasm virtual filesystem automatically.