Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Shaders

Pro

Synthwave Canyon Shader for React

A retro-80s sunset flight drawn on the GPU: a striped sun hangs over a canyon of dark ridges traced in neon, gliding toward you. For React with Base UI, when a music, gaming or retro launch page wants an outrun sunset behind its headline.

Framework

Synthwave Canyon Shader flies you down a canyon at sunset, 80s style. A huge sun fills the top of the frame, yellow at its crown and hot pink at its base, its lower third cut by thin stripes inside a soft corona. In front, ridge after ridge of dark hills, each traced by a glowing lavender line, recede toward the horizon, and the camera wobbles gently as it flies. A WGSL fragment shader on WebGPU draws it with no 3D model: it slices the canyon into 24 flat layers, finds where each pixel's ray crosses each one, and blends them front to back.

Use it behind a music, gaming or retro launch hero, or a full-screen intro with an outrun mood. For a perspective grid in plain CSS, with no GPU, use the Retro Grid background; for a flight through layers of a different kind, Starry Planes Shader; for a daylit landscape, Desert Sand Shader. It runs only while it's on screen, holds a still frame with reduced motion, and shows a gradient of the same sun and haze 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 {
  SynthwaveCanyonShader,
} from "@/components/ui/synthwave-canyon-shader";
<SynthwaveCanyonShader className="flex min-h-[32rem] items-end justify-center pb-16">
  <h1 className="text-5xl font-semibold text-white drop-shadow-[0_0_12px_#ff00ff]">
    Night drive
  </h1>
</SynthwaveCanyonShader>

Synthwave Canyon Shader is a container: your content sits on top of the canyon, 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 scene is the same in both color schemes. White text reads on the purple floor in the lower part, where it passes 4.5:1 on 88% of the area, though the neon lines cross it; on the sun only dark text reads. For a centered headline, put it on a panel such as bg-black/60.

Demos

Scanlines

scanlines draws CRT scanlines across the whole frame, 4 CSS pixels apart at any screen density, for an old-monitor look. speed={0.5} halves the flight, which suits a calmer page.

Preview width

In a card

sunColor="#ff3d81" and glowColor="#00e5ff" turn the sun pink over a cyan horizon, and the glow color tints the sky with it. depth={16} ends the canyon sooner, in the sun's glow, which suits a small box.

Preview width

Performance

About 1.4 ms of GPU time a frame at 1280×720, measured on an M2 Max laptop at full quality. Slower GPUs take several times longer. The ridges cover most pixels within a few slices, so depth saves less than you'd think: depth={12} costs about a fifth less. On a slow GPU, quality={0.5} renders at half resolution, a quarter of the pixels; the neon lines soften but keep their glow. scanlines costs almost nothing. 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 sun and haze, 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 canyon is decoration: it's hidden from assistive tech and lets every click through to your content. With reduced motion it draws one still frame, the sun over the canyon, and stops. Nothing flashes: a new ridge rises out of the haze every two seconds at the default speed, and the lines glide rather than blink, even at speed={3}. For text, white needs the lower part of the frame or a panel: across the flight, white text on a bg-black/60 panel stayed above 5.7:1 everywhere, and black text reads on the sun at 7.9:1 or more on 95% of it.

FAQ

How is the canyon drawn without 3D models?

It's 24 flat slices across the canyon, half a unit apart. Each slice's outline is a ridge profile made of fractal value noise, low in the middle and high at the sides. For every pixel, the shader finds where the ray crosses each slice, front to back, and draws a glowing line at the ridge over a dark fill below it. It stops as soon as the slices in front cover the pixel, so most pixels touch only a few.

Can I change the flight speed?

speed scales the flight: at 1 a new ridge comes in every two seconds, 0.5 halves it, and 0 holds the current frame. paused holds it too, and time draws one fixed moment, in seconds, which is handy for a poster or for tying the flight to scroll.

How do I add CRT scanlines?

Set scanlines. The lines sit 4 CSS pixels apart at any screen density and brighten the frame slightly between them, like an old monitor. They cost almost nothing: one sine per pixel.

Can I recolor the sun?

Yes. sunColor sets the top of the sun and glowColor its base, where the stripes are; the sun blends from one to the other, and the glow color also tints the sky and the corona. Both take any CSS color and follow a theme switch, so var(--primary) works. The ridges keep their own lavender-to-cyan lines.

Will it slow my page down?

It looks 3D, but it's light: about 1.4 ms of GPU time a frame at 1280×720 on an M2 Max laptop, because it crosses flat slices instead of ray marching a surface. It stops when it's off screen. On a slow GPU or a large screen, quality={0.5} renders a quarter of the pixels. A lower depth helps less: depth={12} saves about a fifth.

What happens in browsers without WebGPU?

The box keeps its CSS gradient: a peach and pink sun over plum haze, so the page still looks finished. Nothing errors: the root's data-state reads unsupported.

Credits

Based on Synthwave canyon (opens in a new tab) by mrange, released under CC0, through shadcn.io's version. The HSV-to-RGB conversion is by Sam Hocevar (WTFPL) and the smooth minimum by Inigo Quilez (MIT); both keep their credit lines in the WGSL. What changed in the port:

  • Rewrites the helpers of unknown licence. An integer hash (lowbias32, by Chris Wellons, public domain) and our own value noise replace the sine hash, so the ridges have different shapes than the original's at a given moment; the flight, the canyon and the sun are the same. With the original's hash swapped back in, the port matches the original pixel for pixel. Front-to-back blending replaces the alpha-blend helpers, WGSL's built-in tanh replaces the approximation, and a plain floored modulo cuts the sun's stripes.
  • Drops the 4-second fade-in from black (the canvas fades in itself) and flightHeight, which only cut off the ray above a height and didn't move the camera.
  • Renames crtEffect to scanlines, now sized in CSS pixels so a high-density screen doesn't halve them, and terrainDepth to depth, capped at 24 (more drew nothing new). Adds sunColor and glowColor.

API Reference

SynthwaveCanyonShader

data-slot="synthwave-canyon-shader"

PropType
depth?number

How many ridges ahead are drawn, from 8 to 24: fewer ends the canyon sooner, in the sun's glow, and costs less. @default 24.

Default
24
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.

glowColor?string

The base of the sun, where its stripes are, and the haze over the sky. Any CSS colour. @default "#ff00ff".

Default
"#ff00ff"
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.

scanlines?boolean

Draws CRT scanlines, 4 CSS pixels apart. @default false.

Default
false
speed?number

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

sunColor?string

The top of the sun, which blends into glowColor toward the horizon. Any CSS colour. @default "#ffff00".

Default
"#ffff00"
time?number

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

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