kyokkou (極光)
極光 — polar light. The aurora. Light created at the extremes.
Stack: Rust · egui · egui_graph · Blender 5.1.1 · FalkorDB
Intent in. Raytraced beauty out.
Sibling projects — shared hardware, independent stacks
All three projects run on the same machine (ThinkPad T14s AMD 360 · gfx1150 · 32 GB shared VRAM). Each project manages its own ROCm stack independently — no project depends on another.
| Project | ROCm strategy |
|---|---|
| text-analysis | lemonade-sdk bundled ROCm 7 — fully self-contained |
| klient-t14s | installs ROCm + HIP system-wide — self-contained installer |
| kyokkou | ROCm + HIP inside container — fully isolated |
What it is
An intent-driven visual generator. You pick what you want to create. The tool handles ALL Blender settings — materials, lighting, sim params, camera, render quality. One button: Create image or Create animation.
You never touch Blender directly.
Origin
"I want great Blender creations — raytraced god rays, metal and water reflections, fluid animations through pipes, 3D system maps with information flowing as fluid. A Blender setup acting as Grafana, or a render export feeding my Rust egui engine with live system metrics."
Architecture
Rust egui UI (kyokkou)
→ validated scene_config.yaml
→ bpy scene script (one per scene type)
→ Blender headless (container)
→ PNG / EXR / MP4 / glTF
↑
egui live viewer ←┘
Three layers:
| Layer | Responsibility |
|---|---|
| Rust egui UI | Input, preview, validation, parameter rescaling, render trigger |
| scene_config.yaml | Validated contract — bpy script trusts this completely |
| bpy scene script | One hand-crafted Blender scene, parameterised from yaml |
Fluid strategy
Two distinct methods. Each semantically correct for its category.
| Method | What it is | When |
|---|---|---|
| Mantaflow Liquid | Physics fluid — splashes, pours, chaos | Category 1 art |
| Mantaflow Guide | Directed flow along a curve — no walls, no leaking | Category 2 & 3 info flow |
Guide is the key insight for information flow. The curve is the pipe. No collision geometry → no leaking. The pipe mesh is visual-only — glass tube, conduit, laser channel — zero physics role. The fluid is mathematically steered along the graph edge path. No walls exist, so nothing can leak through them.
This matches the semantics: information doesn't slosh and leak. It travels a path. It has direction and intent. It follows the graph topology.
Speed and colour of each guided flow come directly from edge data — high-traffic connections flow faster, weighted edges glow brighter.
Category 1 — Art scenes
Fluid method: Mantaflow Liquid (physics)
Inputs: Scalars + enums + asset (logo / image)
Validator: Geometry + quality constraints
Preview: Parameter checklist + logo rescale handle
Output: PNG / EXR / MP4
╲ │ ╱
╲ │ ╱
─────[◈]───── ← logo
╱ │ ╲
╱ │ ╲
≋≋≋≋≋≋≋≋≋≋≋≋≋ ← fluid
Scenes:
- Raytraced god rays (volumetric scatter)
- Metal reflections (anisotropic BSDF — gold / steel / chrome / copper)
- Water / liquid (glass BSDF + IOR)
- Fluid through pipes (Mantaflow Liquid)
- Volumetric clouds
- Logo integration — auto-normalised to scene units
Build this first. One hand-crafted Blender scene. Extract 5–8 parameters.
Category 2 — System topology
Fluid method: Mantaflow Guide (information flow)
Inputs: Structural graph — what connects to what
Validator: Graph structure validator
Preview: egui_graph editor — confirm layout before render
Output: PNG / MP4 OR glTF → egui live viewer
●━━━━●━━━━━━━● line A
┃
●━━━━◉━━━● ← interchange
┃
●━━━━━━━● line C
Each graph edge becomes a bezier curve. Guide flow attaches to the curve. The pipe mesh is purely visual — glass tube around the curve, no physics. Information visibly travels the dependency graph, directed and leak-free.
Input sources:
- Draw in egui_graph canvas
- Import JSON / YAML / Terraform / k8s / docker-compose
- Two-way: import → edit → export back to JSON
Graph validator:
- Circular deps → warn, flag in render
- Orphaned nodes → layout decision
- Node count > 50 → readability warning
- Missing labels → fallback to node ID
Category 3 — Data graphs (cinematic portrait)
Fluid method: Mantaflow Guide (information flow)
Inputs: Property graph — nodes and edges carry real values
Validator: Schema + value range validator
Preview: 2D graph + value→visual mapping config
Output: PNG / MP4 OR glTF → egui live viewer
◉━━━━━━◎ ← small
│╲
│ ◉ ← mid
│ ╲
◎━━━━━◉ ← large
The scene:
- Nodes → glass spheres / crystal polyhedra / liquid-metal orbs
- Edges → guided fluid in glass pipes / glowing laser beams / energy arcs
- God rays through the node cluster
- Volumetric fog — edges scatter visibly
- Depth of field — foreground sharp, background bokeh
- Node size + emission = property value
- Edge flow velocity / glow = relationship weight
Value → visual mapping (configured in egui):
- Which property → node size?
- Which property → emission brightness?
- Which property → edge flow velocity / laser intensity?
- All values normalised 0–1 before bpy
Input sources:
- FalkorDB — live Cypher queries
- Neo4j dump / GraphML
- Property graph JSON
Validator concerns:
- Glass caustics > 50 nodes → render time explodes → warn
- Edge count → guide volumes expensive → cap or batch
Live path: FalkorDB → poll → egui graph viewer live. Blender = the beautiful artifact export.
Category 4 — Physical rack topology
Inputs: Server inventory — rack units, slots, hardware specs, cable routes
Validator: Rack geometry validator (U-count, slot conflicts, power budget)
Preview: egui rack diagram — side-on RU view, confirm layout before render
Output: PNG / EXR / MP4 fly-through OR glTF → egui live viewer
╔══════════════╗
║ ▬▬ srvA ● ║ ← cool
║ ▬▬ srvB ● ║ ← normal
║ ▬▬▬ srvC ● ║ ← hot
║ ░░ [empty] ║
╚══════════════╝
The scene:
- Rack chassis as brushed steel enclosure — perforated front panels, cable management arms
- Servers as 1U / 2U / 4U sleds — indicator LEDs emissive, drive bays visible
- Switches → dark anodised aluminium, SFP cages glowing
- Cable runs → colour-coded fibre and copper bundles, volumetric glow on active links
- God rays through server room — dust motes, cold aisle / hot aisle separation visible
- Active ports → pulsing emission
- Load / temp values → heat maps on chassis surfaces
Value → visual mapping (configured in egui):
- CPU load → chassis surface heat map (cool blue → alert red)
- Network utilisation → cable glow intensity
- Power draw → PDU indicator brightness
- Disk activity → drive bay LED pulse rate
Validator concerns:
- U slot overlap → block render, flag conflict
- Power budget exceeded → warn per PDU rail
- Cable count > 100 → batch into trunks for render performance
- Missing U slots → show empty blanking panels
Input sources:
- Netbox REST API — live pull of rack inventory
- Static
rack.yaml— version-controllable, import/export - Manual egui editor — drag servers between U slots
Visual style reference
Network and topology views follow a schematic transit-map aesthetic — lines as routes, nodes as stations, edges carrying weight through thickness and glow intensity. Orthogonal routing, interchange dots, color-coded layers.
●━━━━━━━━━━━●━━━━━● line A
┃
●━━━━━━━━━━━◉━━━━━● line B
┃
● ← interchange
In egui_graph: node size encodes property value, edge thickness encodes relationship weight, color temperature encodes load or state. The Blender export is the cinematic version of the same data.
Scene file format
All egui settings saveable and loadable as a kyokkou scene file — the full UI state: category, scene type, all parameter values, graph layout, value→visual mappings, validator state.
RON — Rust-idiomatic, enums serialize cleanly, comments allowed, readable by power users. Scene files are not machine interchange — readability wins.
// kyokkou scene — art / fluid pipe
(
category: ArtScene,
scene_type: FluidPipe,
metal: Chrome,
render_samples: 512,
output_type: Still,
logo_path: Some("/assets/logo.png"),
logo_scale: 0.42,
)
Scene files load back into egui state exactly. The bpy script still receives
the validated scene_config.yaml — the scene file is the egui side of the contract.
Render backend — bpy in a container
Runtime: Podman. Rust speaks the Podman socket directly via bollard
(DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock). No Docker needed.
bpy (Blender as a Python module, official PyPI package) runs inside a custom container. No suitable off-the-shelf image exists. All custom images share the same Ubuntu base + bpy install + Python HTTP agent. The compute layer is the only difference.
Two deployment scenarios
Scenario A — outside (default): Rust egui runs on the host, agent runs in the container.
HTTP on localhost:8080 bridges them. Rust lifecycle-manages the container via bollard.
Scenario B — bundled: Rust egui + agent both inside the container. One podman run
ships everything. Entrypoint starts the Python agent then exec's the Rust binary.
Display forwarded via Wayland socket or X11 socket at runtime.
Images
| Image | Scenario | GPU | Device passthrough |
|---|---|---|---|
kyokkou-cpu | A | CPU fallback | — |
kyokkou-rocm | A | AMD ROCm/HIP | /dev/kfd, /dev/dri |
kyokkou-cuda | A | NVIDIA CUDA | --gpus all |
kyokkou-bundled-cpu | B | CPU fallback | — |
kyokkou-bundled-rocm | B | AMD ROCm/HIP | /dev/kfd, /dev/dri |
kyokkou-bundled-cuda | B | NVIDIA CUDA | --gpus all |
kyokkou-cpu / kyokkou-bundled-cpu are always the fallback.
ROCm 7.2.2 + HIP confirmed working on host (klient-t14s) — reference for the ROCm images.
Target: Blender 5.1.1 · bpy 5.1
Hardware: kyokkou is not tied to any specific GPU. Cycles runs on whatever the host has — AMD ROCm, NVIDIA CUDA/OptiX, Apple Metal, or CPU fallback. Current development machine is a ThinkPad T14s with Radeon 890M (ROCm), but a 4090 or any CUDA card drops in without code changes — just pass the right device through to the container.
bpy is a Python C extension that IS Blender internals — scene graph, materials, node trees, render engine — loaded directly into a Python process. The only way to drive it from Rust is via an HTTP agent in the same process as bpy. The agent calls bpy functions directly in shared memory.
What the container solves:
- CUDA conflict —
import bpycallscuInit(). egui already holds a GPU context. Container gives bpy exclusive CUDA ownership, full GPU Cycles on the same card. - Host bleed — Python version, numpy, system libs pinned inside. Reproducible renders.
- Python pinning — bpy 5.1 requires exactly Python 3.13. Container owns that.
- GPU/EGL — Cycles full GPU, EEVEE Next via EGL. Pass
--gpus allvia bollard.
Volume mounts set at container creation:
/tmp/kyokkou/in→/in— yaml, textures, assets/tmp/kyokkou/out→/out— PNG, EXR, MP4 output
HTTP agent (stdlib only, no framework) inside the container:
| Endpoint | Behaviour |
|---|---|
GET /health | 200 when bpy is initialised and ready |
POST /render | Accepts yaml body, calls bpy, blocks until done, returns 200 |
POST /upload | Receives texture bytes, writes to /in/textures/, calls bpy.data.images.load() |
Rust owns the container lifecycle via bollard — speaks the Podman socket directly,
no CLI dependency (DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock).
Dependencies: bollard 0.17 · tokio 1 (full) · reqwest 0.12 (json)
Startup: inspect via bollard → running: proceed / stopped: start / missing: create then start.
Poll GET /health until 200, then unlock render button.
Render: validator green → Rust posts yaml to POST /render → tokio task awaits 200 →
reads from /tmp/kyokkou/out → displays in egui preview. egui thread never blocks.
The parameter validator
Every input has a type, range, and cross-parameter constraints. egui shows live checkbox state — green / yellow / red. Render button locked until all checks pass.
Examples:
- Logo scale re-validated when FOV changes
- Volume clouds → quality auto-upgraded to min 512 samples
- Animation mode → disables still-only params
- Glass node graph > 50 nodes → warn, offer simplified material
- FalkorDB values → normalised 0–1 before hitting bpy
- Logo → normalised to scene units, rescale handle in egui preview
The bpy script trusts the yaml. egui guarantees correctness.
egui_graph — first-class editor
- Draw topology from scratch
- Import JSON → auto-layout (force-directed / Sugiyama) → edit
- Export back to JSON — version-controllable
- Drives Blender scene OR live egui viewer directly
What bpy sees — after validation passes
scene_type: clouds_metal_logo
metal: chrome
cloud_type: cumulus
logo_path: /assets/logo.png
logo_scale_scene_units: 0.42
logo_position: [0.0, 0.0, 1.2]
camera_focal_mm: 35
render_samples: 512
output_type: still
output_path: /renders/out.png
Live egui path (no Blender)
FalkorDB (Cypher) → poll → parse property graph
→ egui_graph live viewer → values drive visuals
→ "Export to Blender render" → fires bpy scene
egui = live Grafana-like viewer. Blender = beautiful artifact on demand.
Build sequence
- One hand-crafted bpy scene — beautiful, hardcoded
- Extract 5–8 parameters →
scene_config.yaml - CLI:
kyokkou render --scene fluid_pipe --config scene.yaml - Rust egui UI — writes yaml, live validation
- Validator logic — per parameter, cross-constraints
- Add scene types one by one
Start here: one beautiful fluid pipe scene. Hand-craft it. Make it stunning. Extract the parameters. Everything else follows.