{"id":50514247,"url":"https://github.com/resq-software/viz","last_synced_at":"2026-06-02T22:32:12.223Z","repository":{"id":348865149,"uuid":"1198275750","full_name":"resq-software/viz","owner":"resq-software","description":"3D visualization for ResQ drone simulations — web (Three.js/Cesium) and Unity viewers","archived":false,"fork":false,"pushed_at":"2026-05-28T08:21:46.000Z","size":24394,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T10:08:24.826Z","etag":null,"topics":["aspnet-core","drone-swarm","procedural-generation","real-time-visualization","signalr","tactical-dashboard","three-js","webgl"],"latest_commit_sha":null,"homepage":"https://viz.resq.software/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/resq-software.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-01T09:16:58.000Z","updated_at":"2026-05-28T08:21:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/resq-software/viz","commit_stats":null,"previous_names":["resq-software/viz"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/resq-software/viz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resq-software%2Fviz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resq-software%2Fviz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resq-software%2Fviz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resq-software%2Fviz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/resq-software","download_url":"https://codeload.github.com/resq-software/viz/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resq-software%2Fviz/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33840213,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnet-core","drone-swarm","procedural-generation","real-time-visualization","signalr","tactical-dashboard","three-js","webgl"],"created_at":"2026-06-02T22:32:11.133Z","updated_at":"2026-06-02T22:32:12.207Z","avatar_url":"https://github.com/resq-software.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n  Copyright 2026 ResQ Systems, Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n--\u003e\n\n# ResQ Viz — Live Coordination\n\n**The common operating picture for autonomous disaster response.** Ten agencies show up to a hurricane with ten different drones; one shared air picture keeps them coordinated. ResQ Viz renders that picture in real time — mesh topology, hazard fusion, and decentralized consensus, streaming at 10 Hz into any browser.\n\n- **For emergency managers running multi-agency SAR** — HURRICANE MELISSA scenario, 12 drones across 3 vendors, visible backhaul-loss → mesh-only degradation.\n- **For integration partners** — vendor-tagged chassis per agency, MAVLink mesh simulation, SignalR streaming, REST control plane.\n- **Live:** [viz.resq.software](https://viz.resq.software/)\n\nPress `5` to load `multi-agency-sar`. Press `K` to kill the backhaul. Press `Ctrl+Shift+R` to enter investor-mode for a recorded demo.\n\n---\n\n## Features\n\n- **Live telemetry** — SignalR WebSocket streaming at 10 Hz, ACES filmic tonemapping, PCF soft shadows\n- **5 procedural terrain presets** — each backed by a distinct noise algorithm (domain-warped FBM, ridged multifractal, island-mask, terrace+canyon, anisotropic dunes)\n- **Canvas-drawn tree billboard sprites** — 5-tier pine silhouettes and deciduous blobs rendered with Canvas 2D; 8 triangles per tree, two draw calls for the entire forest\n- **Displaced boulder geometry** — per-vertex hash displacement on IcosahedronGeometry gives every rock a unique craggy profile\n- **Geometry cache with deflate compression** — browser-native `CompressionStream` / `DecompressionStream` (RFC 1951); 572 KB → ~210 KB per preset (~63 % reduction); two-level L1/L2 cache survives page refresh via `sessionStorage`\n- **Post-processing** — selective `UnrealBloomPass` (only emissive LEDs and nav lights glow), `SSAOPass` ambient occlusion, `OutputPass` tone mapping\n- **Unity-style camera** — LMB orbit, RMB free-fly (WASD/QE/Shift), MMB pan, scroll zoom; collision prevention keeps the camera above terrain\n- **Drone interaction** — click to select, WASD/QE to nudge in world space, click terrain to issue GoTo command, `F` to follow\n- **Visual overlays** — position trails, altitude halos, velocity component arrows, mesh topology links, hazard zone discs, detection markers\n- **Settings persistence** — bloom, fog density, FOV, fly speed, trail length, detection rings, battery warning threshold; stored in `localStorage`\n- **WebGPU sensor primitive** — brick-map raymarcher voxelizes the heightfield at boot, then rebuilds when the terrain preset switches or a heightmap override is installed. It serves both **drone-pair line-of-sight** (mesh links visibly fade when terrain occludes them) and **per-drone LiDAR scans** (point clouds emanate from each drone, follow yaw/pitch/roll, optional mast/gimbal mount offsets). One compute kernel, two sensor consumers; ring-buffered async dispatch with `peakSlotDepth` / `raysOutsideWorld` audit counters; press `i` for the live stats overlay.\n- **Lazy-loaded SignalR** — the SignalR runtime ships as a separate ~55 KB chunk and is fetched on first connect, keeping the main bundle below the 800 KiB CI budget\n\n---\n\n## Quick Start\n\n```bash\ngit clone --recurse-submodules \u003crepo\u003e\ncd viz/src/ResQ.Viz.Web\ndotnet run                      # http://localhost:5000\n```\n\n`dotnet run` compiles the TypeScript frontend through Vite automatically via `Vite.AspNetCore` — no separate `npm run dev` is needed.\n\n---\n\n## System Architecture\n\n```mermaid\nflowchart TB\n    subgraph SDK[\"ResQ SDK  (git submodule · lib/dotnet-sdk)\"]\n        ENGINE[\"ResQ.Simulation.Engine\\nphysics · pathfinding · weather\"]\n        MAVLINK[\"ResQ.Mavlink\\nMAVLink gateway · mesh routing\"]\n    end\n\n    subgraph BACKEND[\"ASP.NET Core Host  (src/ResQ.Viz.Web)\"]\n        SIM[\"SimulationService\\nIHostedService · 60 Hz loop\"]\n        VFB[\"VizFrameBuilder\\nsnapshot → VizFrame JSON\"]\n        HUB[\"VizHub\\nSignalR hub\"]\n        REST[\"SimController\\nREST API  /api/sim/*\"]\n    end\n\n    subgraph BROWSER[\"Browser\"]\n        direction TB\n        SC[\"SignalR client\"]\n        subgraph THREEJS[\"Three.js Scene\"]\n            TER[\"Terrain + GeoCache\\n5 preset algorithms\"]\n            DRN[\"DroneManager\\nInstancedMesh · LEDs\"]\n            EFX[\"EffectsManager\\nTrails · Halos · Hazards\"]\n            OVL[\"OverlayManager\\nVelocity · Formation\"]\n        end\n        UI[\"HUD · DronePanel · WindCompass\\nSettings · Controls\"]\n    end\n\n    ENGINE --\u003e SIM\n    MAVLINK --\u003e SIM\n    SIM --\u003e|\"every 6th tick  →  10 Hz\"| VFB\n    VFB --\u003e HUB\n    HUB --\u003e|\"WebSocket  ReceiveFrame\"| SC\n    SC --\u003e TER \u0026 DRN \u0026 EFX \u0026 OVL \u0026 UI\n    UI --\u003e|\"fetch  /api/sim/*\"| REST\n    REST --\u003e SIM\n```\n\n---\n\n## Real-Time Frame Pipeline\n\n```mermaid\nsequenceDiagram\n    participant Eng  as ResQ.Simulation.Engine\n    participant Svc  as SimulationService (60 Hz)\n    participant VFB  as VizFrameBuilder\n    participant Hub  as VizHub (SignalR)\n    participant CLI  as Browser\n\n    loop every 16.7 ms\n        Eng  -\u003e\u003e Svc  : SimulationWorld.Step()\n        alt  every 6th tick  →  10 Hz\n            Svc  -\u003e\u003e VFB  : SnapshotFrame(world)\n            VFB  -\u003e\u003e Hub  : BroadcastFrameAsync(VizFrame)\n            Hub --\u003e\u003e CLI  : ReceiveFrame(JSON)\n            CLI  -\u003e\u003e CLI  : DroneManager.update(drones)\n            CLI  -\u003e\u003e CLI  : EffectsManager.update(frame)\n            CLI  -\u003e\u003e CLI  : OverlayManager.update(drones)\n            CLI  -\u003e\u003e CLI  : HUD.update(count, time, battery)\n        end\n    end\n```\n\n---\n\n## Frontend Module Graph\n\n```mermaid\nflowchart LR\n    APP[\"app.ts\\nentry · wiring\"]\n\n    SCENE[\"scene.ts\\nrenderer · raycasting\"]\n    CAM[\"cameraControl.ts\\nUnityCamera\"]\n    POSTFX[\"postfx.ts\\nbloom · SSAO · output\"]\n\n    TERRAIN[\"terrain.ts\\nheightmap · obstacles\"]\n    PRESETS[\"terrainPresets.ts\\n5 height functions\\n5 GLSL biome shaders\"]\n    GEOCACHE[\"geoCache.ts\\ndeflate-raw L1 / L2\"]\n    SPRITES[\"treeSprites.ts\\nCanvas 2D billboards\"]\n\n    DRONES[\"drones.ts\\nInstancedMesh · LEDs\\nselection · nudge\"]\n    EFFECTS[\"effects.ts\\ntrails · halos · hazards\\nmesh links\"]\n    OVERLAYS[\"overlays.ts\\nvelocity arrows\\nformation lines\"]\n    CONTROLS[\"controls.ts\\nREST panel · keyboard\"]\n\n    UI[\"ui/\\nhud.ts\\ndronePanel.ts\\nwindCompass.ts\"]\n    SETTINGS[\"settings.ts\\nlocalStorage\"]\n    TYPES[\"types.ts\\nVizFrame · DroneState\"]\n\n    APP --\u003e SCENE \u0026 TERRAIN \u0026 DRONES \u0026 EFFECTS \u0026 OVERLAYS \u0026 CONTROLS \u0026 UI \u0026 SETTINGS \u0026 GEOCACHE \u0026 PRESETS \u0026 TYPES\n    SCENE --\u003e CAM \u0026 POSTFX\n    TERRAIN --\u003e PRESETS \u0026 GEOCACHE \u0026 SPRITES\n    DRONES --\u003e TYPES\n    EFFECTS --\u003e TYPES\n```\n\n---\n\n## Terrain Engine\n\nFive presets selectable at runtime from the sidebar. Each builds its own GLSL biome fragment shader, atmosphere (fog colour + density), obstacle distribution, and water level. Switching a preset disposes all Three.js objects and GPU resources, then rebuilds — using the geometry cache when available.\n\n| Preset | Algorithm | Height range | Water level | Character |\n|--------|-----------|-------------|-------------|-----------|\n| 🏔 Alpine | Domain-warped FBM | −60 … +220 m | −3 m | Sweeping ridges, snow caps, 4 mountain peaks |\n| ⛰ Ridgeline | Ridged multifractal | −15 … +210 m | −15 m | Knife-edge ridges, dense conifer valleys |\n| 🏝 Coastal | Island-mask × FBM | −∞ … +90 m | +3 m | Tropical archipelago, sandy beaches |\n| 🏜 Canyon | Terrace + canyon cuts | −80 … +85 m | −60 m | Sandstone mesas, deep gorge networks |\n| 🌵 Dunes | Anisotropic ridge noise | −25 … +60 m | −25 m | Wind-driven barchan dune fields |\n\n```mermaid\nflowchart LR\n    A1[\"Domain-Warped FBM\\nQuilez 2002\\nwarp scale 260 m\\n6-octave final FBM\"] --\u003e P1[\"🏔 Alpine\"]\n    A2[\"Ridged Multifractal\\nMusgrave 1994\\nsignal = 1 − |2n−1|\\n8 octaves, gain 1.8\"] --\u003e P2[\"⛰ Ridgeline\"]\n    A3[\"Island-Mask FBM\\nradial falloff per island\\n× 5-octave topo FBM\\ncoastline perturbation\"] --\u003e P3[\"🏝 Coastal\"]\n    A4[\"Terrace Function\\nsmoothstep(0, 0.18, frac)\\n+ noise threshold\\ncanyon cut depth 80 m\"] --\u003e P4[\"🏜 Canyon\"]\n    A5[\"Anisotropic Ridge Noise\\ntent(n)^2.8 primary axis\\n15° rotated secondary\\nmega-dune field modulation\"] --\u003e P5[\"🌵 Dunes\"]\n\n    subgraph SHARED[\"Shared noise primitives  (terrainPresets.ts)\"]\n        direction LR\n        H[\"_h(ix, iz)\\nWang hash\\nno float drift\"]\n        N[\"_noise(x, z)\\nquintic bilinear\\nC² continuity\"]\n        F[\"_fbm(x, z, oct)\\nfractional Brownian motion\\nlacunarity 2.09  gain 0.47\"]\n        R[\"_ridged(x, z, oct)\\nridged multifractal\\nweight chaining\"]\n        H --\u003e N --\u003e F --\u003e A1 \u0026 A2 \u0026 A3 \u0026 A4 \u0026 A5\n        N --\u003e R --\u003e A2\n    end\n```\n\n---\n\n## Geometry Cache\n\nTerrain vertex positions (572 KB per preset as `Float32Array`) are cached at two levels to make preset switches fast and page reloads instant.\n\n```mermaid\nflowchart TD\n    SW[\"switchPreset(key)\"]\n    L1{{\"L1  In-Memory\\nMap · Float32Array\\n~0 ms lookup\"}}\n    MISS[\"Compute heights\\n48 841 × terrainHeight()\"]\n    UPLOAD[\"BufferGeometry → GPU\\ncomputeVertexNormals()\"]\n    FFW[\"fire-and-forget\\nasync compress\"]\n    CS[\"CompressionStream\\ndeflate-raw  RFC 1951\\n572 KB → ~210 KB  (−63 %)\"]\n    SS[(\"sessionStorage\\nbase64 string\\n~280 KB per preset\")]\n    INIT[\"geoCache.init()\\nat app startup\"]\n    DS[\"DecompressionStream\\ninflate-raw\\n~3 ms\"]\n\n    SW --\u003e L1\n    L1 --\u003e|\"hit\"| UPLOAD\n    L1 --\u003e|\"miss\"| MISS\n    MISS --\u003e UPLOAD\n    MISS --\u003e FFW --\u003e CS --\u003e SS\n    INIT --\u003e SS --\u003e DS --\u003e L1\n```\n\nFive presets cached: ~1.0 MB in `sessionStorage` vs 2.8 MB uncompressed. Compression ratio is logged to the browser console at runtime.\n\n---\n\n## Post-Processing Pipeline\n\nBloom is **selective**: a first composer pass blacks out all non-emissive objects so only drone LEDs, nav lights, and detection markers glow. A blend shader additively composites this onto the full scene render before the final `OutputPass` applies ACES filmic tone mapping and gamma correction.\n\n```\nRenderPass ──► UnrealBloomPass  ──► ShaderPass (blend)  ──► OutputPass\n (bloom          (emissive only)     base + bloom.rgb        ACES + gamma\n  composer)\n\nRenderPass ──► ShaderPass (blend)  ──► OutputPass\n (final          ↑ bloom texture        ACES + gamma\n  composer)\n```\n\n---\n\n## Project Layout\n\n```\nviz/\n├── src/ResQ.Viz.Web/\n│   ├── client/\n│   │   ├── app.ts               Entry point — wires all modules together\n│   │   ├── scene.ts             Three.js renderer, camera, post-processing, raycasting\n│   │   ├── cameraControl.ts     Unity-style free-fly camera with terrain collision\n│   │   ├── postfx.ts            Selective bloom pipeline (two EffectComposer passes)\n│   │   │\n│   │   ├── terrain.ts           Ground mesh, water, trees, rocks, buildings\n│   │   ├── terrainPresets.ts    5 height functions + GLSL biome shaders + obstacle config\n│   │   ├── treeSprites.ts       Canvas 2D tree textures + cross-billboard geometry\n│   │   ├── geoCache.ts          deflate-raw geometry cache (CompressionStream / sessionStorage)\n│   │   │\n│   │   ├── drones.ts            Quadrotor InstancedMesh, PBR materials, LED status, selection\n│   │   ├── effects.ts           Trails, hazard zone discs, detection markers, mesh links\n│   │   ├── overlays.ts          Velocity arrows, altitude halos, formation lines\n│   │   ├── controls.ts          Sidebar REST calls, scenario and command wiring\n│   │   │\n│   │   ├── settings.ts          User settings with localStorage persistence\n│   │   ├── sensorStatsOverlay.ts  Bottom-left dev/audit overlay (`i` to toggle)\n│   │   ├── types.ts             VizFrame · DroneState · HazardState · DetectionState\n│   │   ├── dom.ts               Typed getEl\u003cT\u003e() helper\n│   │   │\n│   │   ├── webgpu/              WebGPU sensor primitive (brick-map raymarcher)\n│   │   │   ├── device.ts          GPUDevice initialization with null-safe fallback\n│   │   │   ├── sensors.ts         bootSensors() — wires world + LoS + LiDAR managers\n│   │   │   ├── registry.ts        Singleton seam — getSensorContext() + LIDAR_MANAGER_CAPACITY\n│   │   │   ├── world.ts           Heightfield → 128³ voxel cube + onTerrainChange rebuild\n│   │   │   ├── brickmap.ts        Sparse top-grid + dense 8³ bricks (BRICK constant)\n│   │   │   ├── los.ts             LosQueryManager — ring-buffered query() with LosQueryStats\n│   │   │   ├── lidar.ts           LidarScan — quaternion-rotated scan pattern + mount offset\n│   │   │   ├── rays.ts            Ray (48 B) / RayHit (32 B) wire format + flag constants\n│   │   │   └── shaders/           build_brickmap.wgsl · march.wgsl · blit.wgsl\n│   │   │\n│   │   ├── __tests__/           Vitest smoke tests (rays packing, LidarScan validation)\n│   │   │\n│   │   └── ui/\n│   │       ├── hud.ts           Top bar — connection, drone count, FPS, battery, selected chip\n│   │       ├── dronePanel.ts    Drone detail panel — position, velocity, battery, commands\n│   │       └── windCompass.ts   Canvas wind rose compass\n│   │\n│   ├── Controllers/             SimController — REST API\n│   ├── Hubs/                    VizHub — SignalR frame broadcast\n│   ├── Models/                  Request / response records\n│   ├── Services/                SimulationService · VizFrameBuilder · ScenarioService\n│   ├── styles/main.css          CSS custom properties, glassmorphism panels, HUD\n│   └── wwwroot/                 Vite build output (gitignored; produced by `dotnet build` and uploaded as the `viz-wwwroot-{sha}` CI artifact for deploys)\n│\n├── tests/ResQ.Viz.Web.Tests/    xUnit + FluentAssertions + Moq\n└── lib/dotnet-sdk/              Git submodule — ResQ .NET SDK\n```\n\n---\n\n## REST API\n\n| Method | Path | Body / Params | Description |\n|--------|------|---------------|-------------|\n| `POST` | `/api/sim/start` | — | Resume simulation |\n| `POST` | `/api/sim/stop` | — | Pause simulation |\n| `POST` | `/api/sim/reset` | — | Clear all drones |\n| `POST` | `/api/sim/drone` | `{ position: [x,y,z] }` | Spawn a drone |\n| `POST` | `/api/sim/drone/{id}/cmd` | `{ type, target? }` | Send flight command |\n| `POST` | `/api/sim/weather` | `{ mode, windSpeed, windDirection }` | Update weather |\n| `POST` | `/api/sim/fault` | `{ droneId, faultType }` | Inject fault |\n| `GET`  | `/api/sim/state` | — | Current drone snapshots |\n| `GET`  | `/api/sim/scenarios` | — | Available scenario names |\n| `POST` | `/api/sim/scenario/{name}` | — | Load a preset scenario |\n\n**Flight commands** — `type` field: `hover` · `land` · `rtl` · `goto` (`goto` requires `target: [x, y, z]`)\n\n**Weather modes**: `calm` · `steady` · `turbulent`\n\n**Scenarios**: `single` · `swarm-5` · `swarm-20` · `sar` · `multi-agency-sar`\n\n---\n\n## Camera \u0026 Controls\n\n| Input | Action |\n|-------|--------|\n| `LMB drag` | Orbit around target |\n| `RMB hold` | Enter free-fly mode |\n| `MMB drag` | Pan |\n| `Scroll` | Zoom |\n| `WASD` | Free-fly strafe / forward · Nudge selected drone (when RMB released) |\n| `Q / E` | Fly up / down · Nudge drone altitude |\n| `Shift` | ×5 speed multiplier |\n| `Click drone` | Select — opens detail panel, activates WASD nudge |\n| `Click terrain` | Send selected drone to that world position |\n| `Click selected drone` | Pass-through to terrain GoTo (re-click = GoTo) |\n\n---\n\n## Keyboard Shortcuts\n\n| Key | Action |\n|-----|--------|\n| `F` | Follow / unfollow selected drone |\n| `Home` | Fit view to entire swarm |\n| `V` | Toggle velocity component arrows |\n| `H` | Toggle altitude halos |\n| `G` | Toggle formation lines |\n| `[` / `]` | Cycle drone selection (severity-sorted to match the telemetry strip) |\n| `Space` | Stop simulation |\n| `R` | Reset simulation |\n| `Tab` | Toggle sidebar |\n| `1` | Scenario: single drone |\n| `2` | Scenario: swarm-5 |\n| `3` | Scenario: swarm-20 |\n| `4` | Scenario: SAR |\n| `5` | Scenario: multi-agency-sar (12 drones across skydio · autel · anzu) |\n| `Shift` + `1` … `5` | Camera presets: overview · tactical · cockpit · ground · investor |\n| `K` | Toggle simulated backhaul kill (mesh-only degradation banner) |\n| `Ctrl` + `Shift` + `R` | Toggle investor-mode cinematic playback for screen recording |\n| `?` | Toggle keyboard shortcuts panel |\n| `i` | Toggle WebGPU sensor-stack stats overlay (queries, peakSlotDepth, raysOutsideWorld) |\n\n---\n\n## Development Commands\n\n```bash\n# Run development server (Vite HMR + ASP.NET Core)\ndotnet run --project src/ResQ.Viz.Web/\n\n# Production build (TypeScript check + Vite bundle → wwwroot/)\ndotnet build src/ResQ.Viz.Web/\n\n# Run xUnit test suite\ndotnet test tests/ResQ.Viz.Web.Tests/\n\n# TypeScript type-check only (no emit)\ncd src/ResQ.Viz.Web \u0026\u0026 npx tsc --noEmit\n\n# Vite bundle only\ncd src/ResQ.Viz.Web \u0026\u0026 npx vite build\n\n# Initialise the SDK submodule after a fresh clone\ngit submodule update --init --recursive\n```\n\n---\n\n## Tech Stack\n\n| Layer | Technology | Notes |\n|-------|------------|-------|\n| Runtime | .NET 10 / ASP.NET Core | `IHostedService` simulation loop |\n| Real-time | SignalR 10 (WebSocket) | 10 Hz frame broadcast; lazy-loaded chunk in the client |\n| 3D | Three.js 0.184 (npm) | PBR, InstancedMesh, custom GLSL |\n| Sensor primitive | WebGPU compute (WGSL) | Brick-map raymarcher; mesh-link LoS + per-drone LiDAR off one kernel |\n| Post-processing | Three.js `EffectComposer` | Selective bloom, SSAO, ACES |\n| Frontend build | TypeScript 6 + Vite 8 | Hot module replacement in dev; rolldown-based |\n| Compression | Web Streams API | `CompressionStream` · `DecompressionStream` · deflate-raw |\n| Simulation | ResQ.Simulation.Engine | Git submodule — physics, terrain, weather |\n| Tests | xUnit + FluentAssertions + Moq | Backend unit tests |\n| Frontend tests | Vitest 4 | Host-side WebGPU primitive smoke tests |\n\n---\n\n## Privacy \u0026 Data Handling\n\nResQ Viz processes only **simulated** drone telemetry. The web app does not:\n\n- collect personal data, account details, or contact information\n- set tracking cookies or run analytics\n- transmit data to third parties\n- store user-generated content beyond an in-memory simulation snapshot\n\nFrame data is broadcast over SignalR to whatever clients are connected to the local server; nothing leaves the host. Because no personal data is processed and no payment or health data is involved, GDPR / HIPAA / PCI-DSS controls are out of scope for this repository. See [SECURITY.md](SECURITY.md) for vulnerability reporting.\n\nA CycloneDX [SBOM](https://cyclonedx.org/) is generated on every push to `main`, every PR, and attached to each release — see the `sbom` workflow.\n\n---\n\n## License\n\nApache-2.0 — Copyright 2026 ResQ Systems, Inc.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresq-software%2Fviz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fresq-software%2Fviz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresq-software%2Fviz/lists"}