Glass & Transparency Effects for the Web
A complete, hands-on guide to building glassmorphism, frosted glass, and Apple Liquid Glass on the frontend — with live demos and copy-paste code for React, Next.js, TypeScript, and vanilla JavaScript.
Glass and transparency define the look of modern interfaces — from the frosted panels of iOS and macOS to the glossy, refractive Liquid Glass Apple now ships across its platforms. This guide gathers every practical way to build these effects on the web, so you can go from concept to production without piecing together a dozen scattered tutorials.
It covers pure-CSS glassmorphism built on backdrop-filter, progressive (gradual) blur, SVG displacement mapping for real refraction and distortion, and full WebGL / Three.js transmission for physically based glass. Every effect below is a live, interactive demo — tweak the controls, watch it update in real time, then copy the exact React, TypeScript, or vanilla JS code.
Liquid Glass Sphere
Apple & Figma-style liquid glass sphere with GLSL refraction, animated color blobs, and organic noise distortion. Pure WebGL — no Three.js required.
Preview
Customize
"use client";
import { useRef, useEffect, useCallback } from "react";
import styles from "./LiquidGlassSphere.module.css";
type Shape = "circle" | "square";
interface LiquidGlassSphereProps {
size?: number; // px, default 240
speed?: number; // time multiplier, default 1.0
ior?: number; // index of refraction, default 1.45
shape?: Shape; // "circle" or "square", default "circle"
className?: string;
}
// Vertex shader: full-screen quad
const VERT = `
attribute vec2 position;
void main() { gl_Position = vec4(position, 0.0, 1.0); }
`;
// Fragment shader: see shader.glsl tab for full source
const FRAG = `/* ... */`;
export default function LiquidGlassSphere({
size = 240, speed = 1, ior = 1.45, shape = "circle", className,
}: LiquidGlassSphereProps) {
const sphereRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const glRef = useRef<{ /* WebGL context + uniforms */ } | null>(null);
const speedRef = useRef(speed);
const iorRef = useRef(ior);
const shapeRef = useRef(shape);
// Update refs without re-initializing WebGL
useEffect(() => { speedRef.current = speed; }, [speed]);
useEffect(() => { iorRef.current = ior; }, [ior]);
useEffect(() => { shapeRef.current = shape; }, [shape]);
const setup = useCallback(() => {
// Create canvas, compile shaders, link program
// Store uniform locations: uResolution, uTime, uIOR, uShape
}, []);
useEffect(() => {
setup();
const ctx = glRef.current;
if (!ctx) return;
const t0 = performance.now();
// 30fps render loop
const loop = (now: number) => {
ctx.gl.uniform1f(ctx.uTime,
((now - t0) / 1000) * speedRef.current);
ctx.gl.uniform1f(ctx.uIOR, iorRef.current);
ctx.gl.uniform1f(ctx.uShape,
shapeRef.current === "square" ? 1.0 : 0.0);
ctx.gl.drawArrays(ctx.gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(loop);
};
// IntersectionObserver to pause when off-screen
// prefers-reduced-motion: render single frame
// Cleanup: loseContext, cancelAnimationFrame
return () => { /* cleanup */ };
}, [setup]);
const borderRadius = shape === "square" ? size * 0.12 : "50%";
return (
<div ref={sphereRef} className={styles.sphere}
style={{ width: size, height: size, borderRadius }}>
<div ref={containerRef} className={styles.canvas}
style={{ borderRadius }} />
</div>
);
}Glassmorphism Card
Classic glassmorphism card with backdrop-filter blur, saturation boost, semi-transparent tint, and subtle border. The most popular glass UI pattern for modern web design.
Preview
Customize
"use client";
import styles from "./GlassCard.module.css";
type GlassCardProps = {
blur?: number;
saturate?: number;
opacity?: number;
radius?: number;
className?: string;
};
export default function GlassCard({
blur = 16,
saturate = 180,
opacity = 0.08,
radius = 16,
className,
}: GlassCardProps) {
const cls = [styles.card, className].filter(Boolean).join(" ");
return (
<div
className={cls}
style={{
borderRadius: radius,
backdropFilter: `blur(${blur}px) saturate(${saturate}%)`,
WebkitBackdropFilter: `blur(${blur}px) saturate(${saturate}%)`,
backgroundColor: `rgba(255, 255, 255, ${opacity})`,
}}
/>
);
}Gradual Blur
A liquid blur effect using layered backdrop filters with gradient masks. Perfect for hero sections, image overlays, and content fades.
Preview
Customize
"use client";
import { useMemo } from "react";
import styles from "./GradualBlur.module.css";
type GradualBlurProps = {
position?: "top" | "bottom" | "block";
strength?: number;
height?: string;
divCount?: number;
zIndex?: number;
inline?: boolean;
className?: string;
};
const DIRECTION = { top: "to top", bottom: "to bottom" } as const;
export default function GradualBlur({
position = "top",
strength = 2,
height = "6rem",
divCount = 5,
zIndex = 100,
inline = false,
className,
}: GradualBlurProps) {
const blurDivs = useMemo(() => {
const increment = 100 / divCount;
const direction = DIRECTION[position];
const divs: React.ReactNode[] = [];
for (let i = 1; i <= divCount; i++) {
const blur = 0.0625 * i * strength;
const p1 = Math.round((increment * i - increment) * 10) / 10;
const p2 = Math.round(increment * i * 10) / 10;
const p3 = Math.round((increment * i + increment) * 10) / 10;
const p4 = Math.round((increment * i + increment * 2) * 10) / 10;
let gradient = `transparent ${p1}%, black ${p2}%`;
if (p3 <= 100) gradient += `, black ${p3}%`;
if (p4 <= 100) gradient += `, transparent ${p4}%`;
const mask = `linear-gradient(${direction}, ${gradient})`;
divs.push(
<div
key={i}
className={styles.layer}
style={{
maskImage: mask,
WebkitMaskImage: mask,
backdropFilter: `blur(${blur.toFixed(3)}rem)`,
WebkitBackdropFilter: `blur(${blur.toFixed(3)}rem)`,
}}
/>
);
}
return divs;
}, [position, strength, divCount]);
const cls = [
styles.container,
inline ? styles.inline : styles.fixed,
className,
]
.filter(Boolean)
.join(" ");
return (
<div
className={cls}
style={{ [position]: 0, height, zIndex }}
>
{blurDivs}
</div>
);
}Three.js Transmission Glass
Realistic 3D glass torus using Three.js MeshTransmissionMaterial with refraction, chromatic aberration, and organic distortion. Built with @react-three/fiber and @react-three/drei.
Preview
Customize
"use client";
import { useRef } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import { MeshTransmissionMaterial, Environment } from "@react-three/drei";
import type * as THREE from "three";
type GlassMeshProps = {
ior: number;
thickness: number;
chromaticAberration: number;
};
function GlassMesh({ ior, thickness, chromaticAberration }: GlassMeshProps) {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((_state, delta) => {
if (meshRef.current) {
meshRef.current.rotation.y += delta * 0.3;
meshRef.current.rotation.x += delta * 0.1;
}
});
return (
<mesh ref={meshRef}>
<torusGeometry args={[1, 0.4, 64, 128]} />
<MeshTransmissionMaterial
transmission={1}
roughness={0}
ior={ior}
thickness={thickness}
chromaticAberration={chromaticAberration}
anisotropy={0.2}
distortion={0.5}
distortionScale={0.3}
temporalDistortion={0.2}
backside
backsideThickness={0.3}
samples={6}
resolution={256}
/>
</mesh>
);
}
type TransmissionGlassProps = {
ior?: number;
thickness?: number;
chromaticAberration?: number;
className?: string;
};
export default function TransmissionGlass({
ior = 1.5,
thickness = 0.5,
chromaticAberration = 0.06,
className,
}: TransmissionGlassProps) {
return (
<div className={className} style={{ width: "100%", height: "100%" }}>
<Canvas camera={{ position: [0, 0, 4], fov: 45 }} dpr={[1, 1.5]} gl={{ antialias: true, alpha: true }} style={{ background: "transparent" }}>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} intensity={1} />
<GlassMesh ior={ior} thickness={thickness} chromaticAberration={chromaticAberration} />
<Environment preset="city" />
</Canvas>
</div>
);
}Apple Liquid Glass
Native Apple Liquid Glass using the -apple-visual-effect CSS property from iOS 26. Works in Safari on iOS 26+ with backdrop-filter blur fallback for other browsers.
Preview
Customize
"use client";
import styles from "./AppleGlass.module.css";
type AppleGlassProps = {
radius?: number;
className?: string;
};
export default function AppleGlass({
radius = 20,
className,
}: AppleGlassProps) {
return (
<div
className={[styles.panel, className].filter(Boolean).join(" ")}
style={{ borderRadius: radius }}
/>
);
}SVG Liquid Distortion
Organic distortion using SVG feTurbulence and feDisplacementMap filters. No WebGL — works everywhere, animates with SMIL.
Preview
Customize
"use client";
import { useId } from "react";
import styles from "./SvgLiquidDistortion.module.css";
type SvgLiquidDistortionProps = {
scale?: number;
frequency?: number;
animate?: boolean;
radius?: number;
className?: string;
children: React.ReactNode;
};
export default function SvgLiquidDistortion({
scale = 30,
frequency = 0.015,
animate = true,
radius = 16,
className,
children,
}: SvgLiquidDistortionProps) {
const id = useId().replace(/:/g, "");
const filterId = `liquid-${id}`;
const freqStr = `${frequency} ${frequency}`;
const freqAnim = `${frequency} ${frequency};${frequency * 1.4} ${frequency * 1.2};${frequency} ${frequency}`;
return (
<div className={[styles.wrapper, className].filter(Boolean).join(" ")} style={{ borderRadius: radius, overflow: "hidden" }}>
<svg className={styles.svg} aria-hidden="true">
<defs>
<filter id={filterId} x="-10%" y="-10%" width="120%" height="120%">
<feTurbulence
type="fractalNoise"
baseFrequency={freqStr}
numOctaves={3}
result="noise"
>
{animate && (
<animate
attributeName="baseFrequency"
values={freqAnim}
dur="6s"
repeatCount="indefinite"
/>
)}
</feTurbulence>
<feDisplacementMap
in="SourceGraphic"
in2="noise"
scale={scale}
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</defs>
</svg>
<div
className={styles.content}
style={{ filter: `url(#${filterId})` }}
>
{children}
</div>
</div>
);
}Frosted Glass Soft Edges
Frosted glass with soft feathered edges using CSS mask-image and radial-gradient. The blur fades smoothly toward the edges instead of having a hard border.
Preview
Customize
"use client";
import styles from "./MaskedGlass.module.css";
type MaskedGlassProps = {
blur?: number;
feather?: number;
radius?: number;
className?: string;
};
export default function MaskedGlass({
blur = 16,
feather = 40,
radius = 20,
className,
}: MaskedGlassProps) {
const inner = 100 - feather;
const maskImage = `radial-gradient(ellipse at center, black ${inner}%, transparent 100%)`;
return (
<div
className={[styles.panel, className].filter(Boolean).join(" ")}
style={{
borderRadius: radius,
backdropFilter: `blur(${blur}px) saturate(180%)`,
WebkitBackdropFilter: `blur(${blur}px) saturate(180%)`,
maskImage,
WebkitMaskImage: maskImage,
}}
/>
);
}SVG Backdrop Refraction
Applies an SVG filter chain (feTurbulence + feDisplacementMap + feGaussianBlur) via backdrop-filter: url(#filter) for glass-like refraction of background content. Chromium only.
Preview
Customize
"use client";
import { useId } from "react";
import styles from "./SvgBackdropRefraction.module.css";
type SvgBackdropRefractionProps = {
blur?: number;
scale?: number;
radius?: number;
className?: string;
};
export default function SvgBackdropRefraction({
blur = 8,
scale = 20,
radius = 16,
className,
}: SvgBackdropRefractionProps) {
const id = useId().replace(/:/g, "");
const filterId = `refract-${id}`;
return (
<div
className={[styles.panel, className].filter(Boolean).join(" ")}
style={{ borderRadius: radius }}
>
<svg className={styles.svg} aria-hidden="true">
<defs>
<filter id={filterId} x="-20%" y="-20%" width="140%" height="140%">
<feTurbulence
type="fractalNoise"
baseFrequency="0.012 0.012"
numOctaves={3}
seed={2}
result="noise"
/>
<feDisplacementMap
in="SourceGraphic"
in2="noise"
scale={scale}
xChannelSelector="R"
yChannelSelector="G"
result="displaced"
/>
<feGaussianBlur
in="displaced"
stdDeviation={blur}
result="blurred"
/>
</filter>
</defs>
</svg>
<div
className={styles.backdrop}
style={{
backdropFilter: `url(#${filterId})`,
WebkitBackdropFilter: `url(#${filterId})`,
}}
/>
</div>
);
}Frequently asked questions
What is glassmorphism and how do I create it in CSS?
Glassmorphism is a UI style that mimics frosted glass: a semi-transparent surface that blurs whatever sits behind it, plus a subtle border and a light tint. In CSS you build it with backdrop-filter: blur(...) saturate(...), a translucent background color, a thin 1px border, and rounded corners. The Glassmorphism Card demo above is a copy-paste starting point with live controls.
What does the CSS backdrop-filter property do?
backdrop-filter applies graphical effects such as blur, saturation, brightness, or contrast to the area behind an element, rather than the element itself. It is the core of every CSS glass effect. It is supported in all current versions of Chrome, Safari, Edge, and Firefox; older Safari needs the -webkit-backdrop-filter prefix, which the code samples include.
How do I recreate Apple's Liquid Glass effect on the web?
Apple's Liquid Glass combines a blurred backdrop with real optical refraction and a glossy highlight. On the web you approximate it by layering backdrop-filter with an SVG displacement map (feImage + feDisplacementMap) or a WebGL/Three.js transmission material for physically based bending of light. See the Apple Liquid Glass, SVG distortion, and Three.js transmission demos above for complete implementations.
Do these glass effects work in React, Next.js, and vanilla JS?
Yes. Every effect ships with React + TypeScript, React + JavaScript, and vanilla JS variants where applicable, so you can drop them into a Next.js app, a Vite project, or a plain HTML page. Switch variants with the tabs on each code block and copy the exact source.
Why is my backdrop-filter blur not working?
The most common causes are: a missing -webkit-backdrop-filter prefix on Safari, a fully opaque background color that hides the blur, or an ancestor with overflow: hidden or a transform/filter that creates a new stacking context and clips the backdrop. Make the element's background semi-transparent and ensure there is actually content behind it to blur.
When should I use CSS, SVG, or WebGL for glass?
Use CSS backdrop-filter for the vast majority of frosted-glass UI — it is the lightest and most compatible. Reach for SVG displacement maps when you need genuine edge refraction or liquid distortion without a 3D engine. Use WebGL/Three.js transmission only when you need physically based glass, thickness, and light transport, since it is the most GPU-intensive option.