Today's web demonstrated
Ten live demos of modern browser capabilities.
Interact with each and observe the code update in real time.
Demo 01 — WebGL2
WebGL: per-pixel light computation
A fragment shader computes per-pixel output using mathematical distance fields. This demo evaluates a metafield from five orbiting points plus your cursor position, rendering surfaces purely from arithmetic. Move through the spawn. Click anywhere to trigger a pulse. The code window updates to show the uniforms flowing into the GPU each frame.
WebGL2 is not available in this browser. The demo is disabled.
// uploaded every frame
gl.uniform2f(u.res, canvas.width, canvas.height);
gl.uniform1f(u.time, 0.00);
gl.uniform2f(u.mouse, 0.00, 0.00);
gl.uniform1f(u.pulse, 0.00);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// in the shader, the pointer is one
// more centre in the field:
// f += (0.06 + pulse * 0.14)
// / dot(p - mouse, p - mouse)
Demo 02 — CSS
Scroll-driven animations
Animation timelines in CSS map motion to scroll progress rather than wall-clock time. The scrollbar becomes a playhead. All animation here uses a named timeline in CSS; the compositor thread runs the motion independently. JavaScript only reads and displays the current scroll percentage.
The progress bar ties to this container's scroll offset. It is a CSS animation on pause; the scrollbar position drives the playhead forward and back.
Every item here animates independently via animation-timeline: view() as it crosses the viewport boundary.
animation-range: entry 0% entry 90% restricts the animation to play only during a specific phase of the element's entry.
Animations run on the compositor thread independent of main-thread load. Heavy JavaScript cannot cause stuttering.
Browsers without animation-timeline receive fallback styling; a small script drives the progress bar via scroll events.
Scroll up to reverse. The timeline responds to motion in both directions.
This browser lacks animation-timeline. A JavaScript fallback is driving the bar.
.scroller {
scroll-timeline: --demo-scroll y;
}
.progress-bar {
animation: fill linear both;
animation-timeline: --demo-scroll;
}
.item {
animation: rise linear both;
animation-timeline: view();
animation-range: entry 0% entry 90%;
}
/* scroll progress: 0% */
Demo 03 — CSS
OKLCH: colour as one number
OKLCH organises colour in perceptually uniform space: lightness, chroma, hue. Colours at the same lightness look the same brightness to human eyes. A single CSS custom property controls the entire palette below; the seven-step tint/shade ladder is computed live with color-mix(). Change the hue slider to watch the theme regenerate from one number.
@property --demo-hue {
syntax: "<number>";
initial-value: 195;
inherits: true;
}
.lab {
--demo-hue: 195;
--demo-accent: oklch(0.62 0.13 var(--demo-hue));
/* = oklch(0.62 0.13 195) */
}
.tint {
background: color-mix(in oklch,
var(--demo-accent), white 50%);
}
Demo 04 — Typography
Variable fonts: width and weight
Variable fonts encode a typeface’s entire design space in a single file. Roboto Flex has multiple axes: weight, width, optical size, grade, and slant. The GPU interpolates letterforms between the defined masters. Adjust the sliders—no new font downloads.
@font-face {
font-family: "Roboto Flex";
src: url("/fonts/roboto-flex/….woff2")
format("woff2-variations");
font-weight: 100 1000;
}
.specimen {
font-variation-settings:
"wght" 400,
"wdth" 100,
"opsz" 14,
"GRAD" 0,
"slnt" 0,
"XTRA" 468;
}
Demo 05 — CSS
Typography: text-wrap
CSS now offers precise control over line-breaking behaviour. text-wrap: balance distributes words evenly across lines for headings. text-wrap: pretty eliminates orphans—short words left alone on the final line. Click a button to apply the value to the paragraph.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
.text {
text-wrap: wrap;
}
/* Valid values: */
/* text-wrap: wrap; — default */
/* text-wrap: nowrap; — no wrapping */
/* text-wrap: balance; — even line lengths */
/* text-wrap: pretty; — avoid orphans/widows */
/* text-wrap: stable; — content-based wrapping */
/* Click a button to test. */
Demo 06 — CSS + Variable Fonts
Drop caps: typographic emphasis with variable fonts
The CSS initial-letter property creates a drop cap—the first letter spans multiple lines. Combine this with variable font axes for dynamic typographic control. Adjust weight and width to see the drop cap transform in real time.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
p::first-letter {
font-family: "Roboto Flex";
initial-letter: 3.8;
font-variation-settings:
"wght" 700,
"wdth" 75;
}
Demo 07 — JavaScript + CSS
View transitions: the DOM morphs
The View Transitions API captures the old page state, applies DOM changes, captures the new state, then animates between the two using the CSS animations you define. The heading has a view-transition-name so it morphs separately. Unsupported browsers swap instantly with no animation.
Lorem
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
This browser lacks the View Transitions API. Swaps are instant.
const swap = () => {
title.textContent = "Lorem";
// …update panel body…
};
document.startViewTransition(swap);
/* the heading animates because of: */
h3 {
view-transition-name: demo-vt-title;
}
// transitions run: 0
Demo 08 — CSS
:has() — the parent selector
The :has() selector reads child state to style parent elements. This was a missing capability in CSS for decades. Below, both cards restyle based purely on input state; CSS does the work. JavaScript only highlights which rule is active in the code window. Toggle the checkbox or enter an email.
.card {
border-color: var(--border);
}
.card:has(input:checked) {
border-color: teal;
translate: 0 -3px;
}
.card:has(input:valid) {
border-color: green;
}
.card:has(input:invalid) {
border-color: red;
}
Demo 09 — CSS
Container queries: components own their breakpoints
Container queries let components respond to their own dimensions rather than the viewport. The dashed box is the container; resize it by dragging the corner. The card inside applies layout rules at 320px and 440px of its width. The viewport size is irrelevant.
One card, three layouts
Stacked below 320px of box width, thumb-left above it, and past 440px the thumb grows and crosses to the right. The viewport plays no part.
.box { container: demobox / inline-size; }
.card {
grid-template-columns: 1fr; /* stacked */
}
@container demobox (min-width: 320px) {
.card { grid-template-columns: 76px 1fr; }
}
@container demobox (min-width: 440px) {
.card { grid-template-columns: 1fr 104px; }
.thumb { order: 2; } /* thumb → right */
}
/* container width: — */
Demo 10 — JavaScript
Your browser, audited live
Runtime feature detection via CSS.supports() and API existence checks. All probes below executed when the page loaded on your browser. Green indicates support; red indicates absence. Click any probe to view the exact test.
Probing…
Probing…
These techniques are available for your project today.