Building an Interactive Underwater Experience with React, WebGL & GLSL
What if a website could feel like water?
DOLPHIN — Beneath the Surface is an interactive underwater web experience that transforms a visual scene into a dynamic digital environment. Instead of using static ripple animations, the experience combines GPU-powered fluid simulation, real-time optical distortion, animated dolphin poses, and responsive water audio.
The goal was to create a lightweight, immersive experience where every cursor movement interacts with the water while the dolphin continues swimming independently.
1. The Technology Behind the Experience
The project combines frontend development with real-time graphics programming.
Core Technology Stack
- React 18: Component-based application structure and UI management.
- Vite 5: Development environment and production build tooling.
- WebGL: GPU-accelerated rendering and real-time visual effects.
- GLSL Shaders: Custom graphics calculations for fluid simulation, refraction, and lighting.
- Web Audio API: Dynamic water sound controlled by cursor movement.
- Vanilla CSS: Layout, typography, responsive styling, and glassmorphism effects.
Rather than relying on a heavy 3D framework, the water rendering system uses raw WebGL to keep the graphics pipeline focused on the effects required by the experience.
2. Creating Real-Time Water Ripples with WebGL
One of the most interesting parts of this project is the interactive water simulation.
Instead of displaying pre-designed ripple images, the water surface is represented as a two-dimensional height field. Cursor movement introduces energy into this field, allowing waves to propagate and gradually fade.
The simulation uses a GPU ping-pong framebuffer technique.
Two render targets alternate between storing the current and next simulation states. A fragment shader samples neighboring pixels to calculate how the water height changes over time.
The simplified wave equation is:
c²\nabla²h-\gamma\frac{\partial h}{\partial t}
]
Where:
- (h) represents the water surface height.
- © controls wave propagation speed.
- (\nabla²h) represents surface curvature.
- (\gamma) controls damping.
The simulation uses a 256 × 256 texture resolution and a damping factor of approximately 0.985.
The result: Cursor movement creates ripples that spread across the surface instead of behaving like isolated animation effects.
3. Turning Cursor Movement into Fluid Motion
A simple mouse-position calculation can create separate ripple circles, especially when the cursor moves quickly.
To achieve smoother water trails, the simulation calculates the distance between the cursor’s previous and current positions using a line-segment approach.
This allows the shader to inject energy along the cursor’s movement path.
A smooth falloff function controls the ripple intensity based on distance and cursor speed.
This creates continuous water wakes rather than disconnected ripple points.
4. Simulating Optical Refraction with GLSL
Real water does more than create waves. It changes how objects appear beneath its surface.
To recreate this effect, the rendering shader uses the water height field to calculate surface gradients and estimate the water surface normal.
These values are then used to displace the background texture’s UV coordinates.
The rendering pipeline includes:
- UV Displacement: Shifts background sampling coordinates based on wave movement.
- Chromatic Dispersion: Applies slightly different sampling offsets to red, green, and blue channels.
- Surface Lighting: Adds specular highlights to simulate sunlight reflecting from the waves.
- Aspect Ratio Correction: Keeps the background composition proportional across different screen sizes.
This makes the underwater environment appear as though it is being viewed through a moving water surface.
5. Animating the Dolphin Without Mouse Dependency
The dolphin has its own independent animation system.
Four dolphin pose images are loaded as separate WebGL textures. The rendering system cycles through these images to create a continuous swimming effect.
Get TJ’s stories in your inbox
Join Medium for free to get updates from this writer.
The animation uses two timing stages:
- Pose Hold: Each pose remains visible for approximately 2.4 seconds.
- Smooth Transition: A crossfade transition takes approximately 0.8 seconds.
A smoothstep interpolation function blends the current and next pose.
Most importantly, the dolphin animation is independent of cursor movement. The cursor controls the water simulation, not the dolphin’s position or direction.
This separation keeps the interaction natural and predictable.
6. Adding Cursor-Responsive Water Audio
Visual interaction is only one part of the experience.
The project also uses the Web Audio API and an ambient water recording to create a more immersive environment.
Cursor velocity controls the target audio volume:
- Faster movement increases the water sound.
- Slower movement reduces the sound.
- When the cursor stops, the audio gradually fades towards silence.
Volume interpolation prevents abrupt changes and creates smoother audio transitions.
An audio toggle in the navigation allows visitors to control the sound manually.
Because modern browsers restrict autoplay, audio playback begins only after the visitor interacts with the page.
7. Building the Interface with React and CSS
The visual interface is intentionally minimal so the underwater environment remains the main attraction.
React manages the application structure, while CSS handles the interface and layout.
The design uses:
- Glassmorphism-inspired navigation.
- Minimal typography.
- Transparent interface elements.
- Responsive viewport layouts.
- Subtle visual hierarchy.
The interaction layer is kept separate from the interface so the water canvas can receive pointer events without unnecessary UI interference.
8. Performance and Optimization
Real-time graphics require careful resource management.
Some important implementation considerations include:
- Using GPU framebuffers for repeated simulation calculations.
- Reusing preloaded textures instead of loading images during animation.
- Using
requestAnimationFramefor rendering updates. - Keeping simulation resolution controlled.
- Disposing of WebGL textures, framebuffers, and event listeners when components are removed.
- Handling browser audio restrictions and touch interaction.
These techniques help keep the rendering pipeline efficient. Actual frame rate and bundle size should be measured on target devices rather than assumed.
9. What I Learned
Building DOLPHIN was an opportunity to explore the connection between frontend development and real-time graphics.
The project involved working with:
- React component architecture.
- WebGL rendering pipelines.
- GLSL fragment shaders.
- GPU-based wave simulation.
- Texture sampling and UV manipulation.
- Smooth interpolation and animation timing.
- Web Audio API.
- Interactive experience design.
It also demonstrated how mathematical concepts and graphics programming can turn a simple website into something visitors can actively explore.
Experience It Yourself
The best way to understand this project is to interact with it.
Move your cursor across the water, watch the ripples spread, and explore the underwater world.
An experiment in creative frontend development, interactive graphics, and the beauty of moving water.







