Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Bioluminescence Shader for React

Night surf drawn on the GPU: slow waves roll onto a dark beach, their breaking crests light up electric cyan and fade, and the wet sand mirrors the glow. For React with Base UI, when an ocean, marine or resort hero needs a dark, calm scene with a spark of light.

Framework

Bioluminescence Shader draws a beach at night, seen from low down. Slow waves roll in under a starless navy sky, and wherever a crest breaks, plankton in the water light up electric cyan along its lip, then fade to black behind it within a couple of seconds. Near the shore the glow runs a little way up the sand, and the wet sand mirrors it. A WGSL fragment shader on WebGPU maps each pixel below the horizon to a point on the beach, then lights it from three sets of wave crests that cross at slight angles, each bent by noise and breaking in sections.

Use it behind an ocean nonprofit, a marine-biotech or a resort hero, or any dark page that wants a calm scene with a spark of life. For a daylit sea, use Sea Shader; for the same night in plain CSS, with no GPU, the Deep Sea background has glowing jellyfish instead of surf. The sky stays dark and empty for your headline. It runs only while it's on screen, holds a still frame with reduced motion, and shows a gradient of the same night and surf in browsers without WebGPU.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import {
  BioluminescenceShader,
} from "@/components/ui/bioluminescence-shader";
<BioluminescenceShader className="flex min-h-[32rem] flex-col items-center pt-24">
  <h1 className="text-5xl font-semibold text-white">Into the blue</h1>
  <p className="mt-3 text-slate-400">Protecting the reefs since 1998.</p>
</BioluminescenceShader>

Bioluminescence Shader is a container: your content sits on top of the beach, in normal flow. It fills its own box, so give it a height or content. For a backdrop behind the whole page, render it empty with className="fixed inset-0 -z-10". The night is the same in both color schemes, so use light text: put the headline in the sky, the top 42% by default, where white text stays above 18:1 and text-slate-400 above 7:1. Over the sea, white still reads on 93% of the area, but not over a bright crest.

Demos

Green plankton

glowColor="#a6ff4d" turns the surf the green of some plankton blooms, and the brightest crests still run to white. afterglow={0.8} lets each crest leave a longer trail, about three seconds, so more of the sea glows at once.

Preview width

In a card

horizon={0.3} raises the horizon so the beach fills more of a small box, and waves={2} keeps two sets of crests, a calmer sea that doesn't crowd the card.

Preview width

Performance

About 0.7 ms of GPU time a frame at 1280×720, measured on an M2 Max laptop at full quality. Slower GPUs take several times longer. Each set of waves adds about 0.15 ms, so waves={2} is a little cheaper; on a slow GPU, quality={0.5} renders a quarter of the pixels, and the soft glow hides it well. The animation stops when the shader scrolls out of view, and the browser pauses it in a hidden tab. Every shader on a page shares one GPU device and one frame loop, and vgpu loads only when the first one mounts.

Browser support

The shader needs WebGPU: Chrome and Edge, Safari 26 and later, and Firefox on Windows and macOS. Elsewhere, and until the first frame, the box shows a CSS gradient of the same scene, a navy sky over dark water with two soft cyan glows near the shore, so the layout never jumps. data-state on the root reads unsupported there, if you want to style around it. See Shader Canvas.

Accessibility

The beach is decoration: it's hidden from assistive tech and lets every click through to your content. With reduced motion it draws one still frame, two crests breaking near the shore, and stops. Nothing flashes: a crest brightens over seconds as it nears the shore, and any one point of the sea lights up about once every three seconds at the default speed, once a second at speed={3}. For text, the sky is the place: white text there stays above 18:1 on every frame we measured.

FAQ

How is it drawn?

There's no 3D model. Each pixel below the horizon maps to a point on a flat beach, farther away the closer it is to the horizon. Three sets of wave crests roll toward you across it, at slight angles to each other, bent by noise. Each crest is treated as a low wall: the pixels just past it see its face, which glows toward a bright lip, and behind it the plankton it stirred fade out. Crests light up only near the shore, in sections that shift along the wave, and the wet sand reflects the surf beyond it.

How do I make the glow linger longer, or shorter?

afterglow sets how long the water keeps glowing after a crest passes: at 0 only the lip glows, at 0.5 (the default) the trail fades in about two seconds, and at 1 it lasts about four. foam sets how much of that trail lights up, from scattered sparks at 0 to an almost solid sheet at 1.

Can I change the colors?

Yes. glowColor is the light of the plankton; the brightest crests run to white whatever you pick. skyColor, waterColor and sandColor set the night, the sea and the dry sand, and the wet sand is a darker, glossy shade of the sand. All four take any CSS color and follow a theme switch, so var(--primary) works for the glow. Keep the others dark: the glow only shows against a night.

Can I move the horizon or calm the sea?

horizon sets where the sky ends, as a fraction of the height from the top, from 0.2 to 0.6; a higher value gives the beach more room, a lower one leaves more sky for text. waves sets how many sets of crests cross, from 1 (one slow, regular swell) to 4 (a busier sea), and intensity dims or brightens the glow, from 0 to 2.

Will it slow my page down?

It's light: about 0.7 ms of GPU time a frame at 1280×720 on an M2 Max laptop, and it stops when it's off screen. waves is the only prop that changes the cost, by about 0.15 ms per set. On a slow GPU or a large screen, quality={0.5} renders a quarter of the pixels, and the soft glow hides the difference.

What happens in browsers without WebGPU?

The box keeps its CSS gradient: a navy sky over dark water, with two soft cyan glows near the shore, so the page still looks finished. Nothing errors: the root's data-state reads unsupported.

API Reference

BioluminescenceShader

data-slot="bioluminescence-shader"

PropType
afterglow?number

How long the glow lingers behind each crest, from 0 (a thin line) to 1 (a long trail, about 4 seconds). @default 0.5.

Default
0.5
fallback?string

What shows until the first frame and in browsers without WebGPU: a CSS background value (gradients, sizes, a colour) that looks like the shader. It can use the colour props' --shader-<field> variables.

foam?number

How much of the surf glows, from 0 (sparse sparks) to 1 (a solid sheet of light). @default 0.6.

Default
0.6
glowColor?string

The light of the plankton; the brightest crests run to white. Any CSS colour. @default "#3ff5ff".

Default
"#3ff5ff"
horizon?number

Where the horizon sits, as a fraction of the height from the top, from 0.2 to 0.6. @default 0.42.

Default
0.42
intensity?number

How bright the glow is, from 0 to 2. @default 1.

Default
1
interactive?boolean

Whether u.pointer follows the pointer. @default false.

paused?boolean

Holds the current frame. @default false.

quality?number

Render scale, from 0.25 to 1, of the screen's pixels (capped at 2×). @default 1.

sandColor?string

The dry sand in the foreground; the wet sand is a darker, glossy shade of it. Any CSS colour. @default "#0d0f14".

Default
"#0d0f14"
skyColor?string

The night sky, a little lighter at the horizon. Any CSS colour. @default "#050913".

Default
"#050913"
speed?number

Animation speed: 1 is normal, 0 holds the frame. @default 1.

time?number

A fixed moment, in seconds: draws it and doesn't animate.

waterColor?string

The dark sea between the breakers. Any CSS colour. @default "#04101c".

Default
"#04101c"
waves?number

How many sets of waves cross each other, from 1 to 4. @default 3.

Default
3
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Forwarded to the part.

All 101 shaders