Joshua Opolko

VR Visual Effects That Exploit the Visual Cortex: 4 Psychedelic Techniques

Four live WebGL shaders, each targeting a different visual cortex mechanism. Sliders change shader parameters in real time. Set them before tapping Enter VR since controls are hidden in immersive mode.

1. Moiré interference grid (emergent motion)

Exploits: V1 spatial frequency channel beating + apparent motion without source movement

Three.js WebGL
Core GLSL (fragment shader)
vec2 p = (vUv-0.5); p.x *= uRes.x/uRes.y;
float t = uTime*uAngleSpeed*0.3;
vec2 g1 = p*uScale;
float grid1 = sin(g1.x*6.283)*sin(g1.y*6.283);
float ca=cos(t),sa=sin(t);
vec2 g2 = vec2(p.x*ca-p.y*sa, p.x*sa+p.y*ca)*uScale*1.037;
float grid2 = sin(g2.x*6.283)*sin(g2.y*6.283);
float moire = grid1*grid2;
float amp = moire*0.5+0.5;
vec3 col = hsv2rgb(vec3(amp*0.5+uTime*0.03, uSaturation, pow(amp,0.65)));
gl_FragColor = vec4(col,1.0);
AI demo prompt

Create a self-contained Three.js r169 WebGL demo showing a psychedelic moiré interference field. Fullscreen quad, ShaderMaterial. Fragment: two sinusoidal grids — grid1 = sin(p.x*scale*6.283)*sin(p.y*scale*6.283), grid2 same but rotated by uTime*uAngleSpeed and scaled by 1.037. Multiply grids for interference value. HSV color from interference amplitude cycling with uTime. uScale, uAngleSpeed, uSaturation sliders. 300px canvas.

2. Infinite tunnel (depth cue overload)

Exploits: looming response + vergence-accommodation conflict

Three.js WebGL
Core GLSL (fragment shader)
vec2 uv = vUv * 2.0 - 1.0; uv.x *= uRes.x / uRes.y;
float r = length(uv); float angle = atan(uv.y, uv.x);
float depth = 0.4 / (r + 0.005);
float td = mod(depth - uTime * uRushSpeed, 1.0);
float ta = mod(angle / (PI / uSymmetry), 1.0);
float stripes = sin(ta*28.0+uTime*0.5)*0.5+0.5;
float rings   = sin(td*22.0-uTime*2.0)*0.5+0.5;
vec3 col = hsv2rgb(vec3(td*0.55+uTime*0.07, 0.97, 1.0))
           * stripes * rings * uBrightness;
col *= 1.0 - smoothstep(0.6, 1.05, r);
gl_FragColor = vec4(col, 1.0);
AI demo prompt

Create a self-contained Three.js r169 WebGL demo showing a psychedelic infinite tunnel. Fullscreen quad, ShaderMaterial. Fragment: polar coordinate mapping, depth = 0.4/r, animate with mod(depth - uTime*uRushSpeed, 1.0). uSymmetry angular sectors. Stripe and ring interference. HSV color cycling. uRushSpeed, uSymmetry, uBrightness sliders. Vignette at edges. 300px canvas.

3. Displacement mapping (breathing surfaces)

Exploits: surface normal processing (V2/V3) + proprioceptive conflict

Three.js WebGL
Core GLSL (vertex shader)
vec3 pos = position; float ws = uWaveSpeed;
float h = sin(pos.x*2.2+uTime*0.6*ws) * sin(pos.y*2.2+uTime*0.45*ws) * uAmplitude;
h += sin(pos.x*6.0+uTime*1.3*ws) * sin(pos.y*4.5+uTime*1.1*ws) * uAmplitude * 0.33;
h += sin((pos.x+pos.y)*5.5+uTime*1.9*ws) * uAmplitude * 0.16;
pos.z += h; vH = h;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
AI demo prompt

Create a self-contained Three.js r169 WebGL demo showing a psychedelic breathing displacement surface. PlaneGeometry(4,4,64,64), perspective camera looking down at an angle. ShaderMaterial: vertex displaces Z with 3 sin() wave frequencies * uAmplitude * uWaveSpeed. Fragment: HSV color-by-height with uTime hue cycling, plus fract(vUv*uGridDensity) grid overlay. uAmplitude, uWaveSpeed, uGridDensity sliders. 300px canvas.

4. Polar sine field (rotational standing wave)

Exploits: V1 orientation selectivity + pattern completion loop

Three.js WebGL
Core GLSL (fragment shader)
vec2 p = vUv*2.0-1.0; p.x *= uRes.x/uRes.y;
float r = length(p); float a = atan(p.y,p.x);
float radial  = sin(r*uRadFreq - uTime*uSpeed);
float angular = sin(a*uAngFreq + uTime*uSpeed*0.43);
float diag    = sin((r*uRadFreq*0.7 + a*uAngFreq*0.5)*1.2 + uTime*uSpeed*0.6);
float field   = (radial*angular + diag*0.6)/1.6;
float brightness = field*0.5+0.5;
vec3 col = hsv2rgb(vec3(field*0.4+r*0.1+uTime*0.05, 0.97, brightness));
col *= 1.0-smoothstep(0.82,1.05,r);
gl_FragColor = vec4(col,1.0);
AI demo prompt

Create a self-contained Three.js r169 WebGL demo showing a psychedelic polar sine interference field. Fullscreen quad, ShaderMaterial. Fragment: polar coords, three overlapping sine waves: radial = sin(r*uRadFreq - uTime*uSpeed), angular = sin(a*uAngFreq + uTime*uSpeed*0.43), diagonal = sin((r*uRadFreq*0.7 + a*uAngFreq*0.5)*1.2 + uTime*uSpeed*0.6). Combine and map to HSV. uRadFreq, uAngFreq, uSpeed sliders. Vignette. 300px canvas.


About these techniques

1. Moiré interference grid

Two sinusoidal grids at slightly different scales overlap, producing a low-frequency beat pattern that appears to travel across the field even though neither underlying grid moves fast. V1 contains dedicated spatial-frequency detectors tuned to specific grating wavelengths. Two slightly different wavelengths produce a beat at their difference frequency, generating motion signals in MT/V5 from sources that are not actually moving. Scale sets grid density; Angle Speed rotates one grid against the other; Saturation controls color depth.

2. Infinite tunnel

A tunnel rendered in polar coordinates with continuously advancing depth creates persistent looming motion the brain cannot habituate to, because it never arrives. The vergence-accommodation system drives the eyes to converge on approaching geometry and finds nothing solid. Rush Speed controls how fast the tunnel appears to advance; Symmetry sets angular sector count; Brightness scales overall output without changing the color balance.

3. Displacement mapping

Animated vertex displacement across a 64x64 mesh creates the "breathing floor" effect. V2 and V3 cortical areas process surface curvature and depth discontinuities; a gently undulating mesh creates continuous non-zero input with no stable resting state. The grid overlay makes the vertex density visible. In Quest VR with head tracking, tilting your head toward the mesh amplifies the proprioceptive conflict between visual motion and vestibular stillness.

4. Polar sine field

Three overlapping sine waves in polar coordinates (radial, angular, and diagonal) generate a lattice of nodes that appears to rotate, breathe, and reorganize simultaneously. V1 contains orientation-selective columns tuned to specific grating angles. When three orientations are simultaneously active at full saturation, the cortex attempts to form a stable percept, identifies a pattern, then finds it has shifted before the identification completes. Radial Freq controls ring density; Angular Freq sets sector count; Speed drives all three waves.

Key takeaways

Why Three.js for browser VR

Three.js r169 exposes raw GLSL shader control via ShaderMaterial, handles WebXR stereo rendering for the Quest browser natively, and is deliverable as a single HTML file with CDN imports. Babylon.js is a viable alternative with stronger built-in scene management for complex multi-object environments. For raw shader VFX with minimal overhead and the highest quality of AI code generation, Three.js wins on simplicity. No build pipeline. No install. The demos on this page load Three.js from jsDelivr CDN and initialize lazily as you scroll.

Which VR headsets can run these demos?

All four demos use the WebXR Device API immersive-vr session type with no proprietary SDK or native install.


What is the WebXR performance budget on Meta Quest?

TechniqueTypeQuest 3 (90Hz)Quest 2 (72Hz)
Infinite tunnelFragment shader1-2ms2-3ms
Displacement (64x64)Vertex + fragment2-3ms3-4ms
Moiré interference gridFragment shader1ms1-2ms
Polar sine fieldFragment shader1ms1-2ms
Practical stack (3-4 combined)6-9ms9-13ms

Quest 2 budget is 13.9ms total per frame. Combining more than 3-4 effects requires adaptive quality: monitor performance.now() per frame and step down shader complexity (reduce loop iterations, halve resolution) if frame time exceeds 11ms. Quest 3 has approximately 2x GPU throughput, making 4-5 combined effects feasible at 90Hz.


Frequently asked questions

What makes VR visual effects psychedelic?

Psychedelic VR effects work by simultaneously overwhelming multiple visual cortex processing streams: motion detection in MT/V5, color processing in V4, scale-invariant pattern recognition, and temporal integration. The visual cortex dedicates 50-55% of the cerebral cortex to vision, making it highly susceptible to calibrated perceptual overload. See the full neuroscience breakdown.

Can I run these VR effects in the browser without Unity?

Yes. Three.js with ShaderMaterial exposes raw GLSL and supports the WebXR Device API natively in Meta Quest Browser. All 4 demos on this page are self-contained. On Quest, open this page in the browser, scroll to any demo, and tap Enter VR to start an immersive-VR session with stereo rendering and head tracking.

How do I enter VR from the browser on Meta Quest?

Open this page in Meta Quest Browser. Scroll to any demo and tap the Enter VR button below the canvas. The browser requests permission for an immersive-vr session via the WebXR Device API. The demo renders in full stereo at the Quest's native refresh rate (72Hz on Quest 2, 90Hz on Quest 3). Press the menu button to exit and return to the page.

What is the performance budget for psychedelic VR on Meta Quest?

Quest 3 targets 90Hz (11.1ms frame budget); Quest 2 targets 72Hz (13.9ms). Fullscreen GLSL fragment shaders cost 2-5ms. GPU particles cost 1-3ms. Running 3-4 combined effects is feasible with adaptive quality reducing shader iteration counts at runtime when frame time exceeds 11ms.

Which visual effects cause the strongest perceptual response in VR?

Infinite tunnel effects are consistently the strongest because they exploit the looming response, vergence-accommodation conflict, and forward self-motion signals simultaneously. Combining any two techniques produces the most intense perceptual response within a safe frame budget.

Which VR headsets can run these WebXR demos?

Any headset with a WebXR-capable browser. Meta Quest 2, 3, 3S, and Pro work natively via Meta Quest Browser. Pico 4 and Pico 4 Ultra work via Pico Browser with the same Enter VR flow. PC VR headsets including Valve Index, HTC Vive Pro 2, HP Reverb G2, Varjo XR-4, and Pimax Crystal work by opening Chrome or Edge on the connected Windows PC while SteamVR is running: WebXR routes automatically through SteamVR's OpenXR runtime with full stereo rendering and head tracking. The shaders are fragment-bound, so higher-resolution headsets like Varjo and Pimax resolve finer interference and tunnel detail at no additional GPU geometry cost. Apple Vision Pro does not currently support immersive-vr WebXR sessions in Safari.


Sources