// Problem
Revocure measures the user's physiological state during breathing and relaxation exercises. A number on a screen doesn't work - someone inside a headset isn't looking at charts, they're looking at the world around them.
What was needed was feedback inside the scene itself: the environment should reward calm, balanced breathing and dim down when the user is dysregulated. All within a standalone VR frame budget, with no extra CPU load.
// Data pipeline
The path from a chest strap to a material parameter in the shader:
// Shader implementation
A single normalized parameter propagates through the scene and drives several visual layers at once, so the reaction reads as one coherent response instead of scattered individual effects.
- Vegetation - leaves and vines unfurl and gain saturation as the parameter rises; at low values they wilt and desaturate.
- Atmosphere - fog density, god rays and weather effects move the scene from heavy and muted to bright and open.
- Visual reward - sustaining high HRV brings in extra elements (rainbow, particles) that close the feedback loop.
How it is built
Both shaders are written by hand in HLSL as custom URP Lit variants - no Shader Graph. One is opaque, for terrain and rock; the other is an alpha-cutout, two-sided companion for leaves, flowers and vines.
- One global float - the controller pushes the normalized HRV value once per frame with
Shader.SetGlobalFloat. Every shader in the scene reads it, so nothing has to be wired up per object and no material gets instanced just to receive the value. - An interface for the rest - systems a shader cannot reach (VFX, post-process volume, animators) implement a small
IHRVDriveninterface and receive the same value, so the whole scene moves as one. - Vertex animation on the plants - sway across XZ plus a subtle pulse in scale, with both amplitudes interpolated between a calm and an active preset by the HRV value. Calm breathing barely moves the leaves; a high reading makes the whole garden breathe.
- Anchored roots, desynchronised plants - a height mask taken from object space keeps the base of each plant still and lets the tips travel furthest, while a phase offset derived from world position stops every plant swaying in lockstep.
- Shadows and depth follow the animation - the same vertex displacement is duplicated in the ShadowCaster and DepthOnly passes, so shadows track the moving mesh instead of sitting under a static silhouette. The alpha cutout is applied in the shadow pass too, so holes in a leaf produce holes in its shadow.
- Tunable outside Play Mode - a debug value drives the same path through
OnValidate, so the look can be dialled in directly in the editor without entering play and without a headset on.
// Standalone optimization
- GPU-side driving - the reaction happens in the shader, not through scripts moving objects around. The CPU only supplies one number per frame.
- One global parameter - instead of instancing materials per object, all vegetation reads the same value, with no draw call multiplication.
- Smoothing instead of switching - continuous interpolation over time rather than jumps between states, avoiding recomputation and visible popping.
// Result
The scene responds to live HRV data within the standalone Quest frame budget. The whole reaction is computed in the shader on the GPU - the CPU feeds one value per frame, and the vegetation reads a single global parameter instead of instancing materials per object.
// Video
The scene reacting to a change in the HRV parameter: