XR Environment
A virtual waiting-room backdrop: a night gradient sky, 40 randomly placed stars each twinkling at its own pace, a grid floor tilted in perspective across the bottom 40%, and two gradient squares, purple and pink, floating above the horizon. A "virtual space" mood in CSS and 14 lines of JavaScript — no headset, no WebXR, no interaction.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. 3D has 50 effects, including 8 free. Explore the category →
3 usage examples



How it works
The root .fx-0050-root (#0a0a0f background, at least 300 px tall) holds the .xr-environment backdrop (full width, at least 300 px, overflow: hidden), painted with a vertical #0a0a2e → #1a1a3e → #2a2a4e gradient. The .xr-env-ground floor takes the bottom 40%, underlined by an indigo line; it carries a 300 px perspective, and its .xr-env-grid-floor grid (10% indigo lines every 30 px) is laid down by rotateX(60deg) from its top edge: the lines tighten toward the horizon. The two .xr-env-obj objects are 30 px squares with rounded corners, placed at 30% and 60% of the width, just above the horizon (42% from the bottom), rising and falling 10 px over 3 s, the second one a second behind.
The script creates 40 two-pixel .xr-env-star elements in every .xr-env-stars found under a .fx-0050-root root: random position across the width and the top 60%, a twinkle (opacity from 0.3 to 1) lasting a random 1.5 to 3.5 s and starting a random 0 to 3 s late, so no star beats in time with its neighbor. Each star also gets a random fixed opacity, which the animation covers and which is used when motion is reduced. Measured in Chromium 151, Firefox 153 and WebKit 26: two copies of the effect on a page each get their 40 stars, and 0.35 to 0.41% of the pixels change over 0.8 s.
Since positions are random, the sky changes on every load: that suits a mood backdrop, but two captures are never identical. The catalog name says "XR": nothing here talks to a headset or to WebXR, the "3D objects" are flat squares and the sky is a gradient, not a skybox. It is the picture of a virtual lobby, made for a web page. Low cost: 42 small opacity and transform animations, no JavaScript loop once the stars are placed.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, a block scoped to.fx-0050-rootstops the twinkling and the floating: the sky stays dotted with stars of different brightness (the fixed opacity set by the script) and the two squares wait still above the horizon. Measured: 0 animations and 0% of pixels changed over 0.8 s in Chromium, Firefox and WebKit. - Backdrop: no text, no information, no interaction; the stars and squares are empty
divelements that screen readers skip. Any text you lay on top (a time, a title, an instruction) stays normal text, as in the scenes above. - Twinkling: 40 two-pixel dots whose opacity changes gently over 1.5 to 3.5 s, never all at once: no overall blinking and no flash, and the scene's overall brightness does not change.
- Contrast of text laid on top: the sky goes from
#0a0a2eto#2a2a4e; white text there is above 13:1 (calculated on the lightest shade). The white stars fill the top 60%: small light gray text would get lost among them, so prefer white text that is large enough or set on a card, as in scene 3.
Browser compatibility
CSS (gradients, opacity and transform animations, perspective and rotateX for the floor, inset) + 14 lines of JavaScript (star creation), no dependencies. Measured in Chromium 151 and Firefox 153: tilted floor, 40 stars per copy, two independent copies, stop under reduced motion. In WebKit 26, stars, objects and the stop are identical, but Playwright's WebKit flattens all CSS 3D: the tilted floor in Safari has not been verified on a real Safari.
Without JavaScript, the sky has no stars: gradient, floor and floating objects remain. Without 3D transforms, the floor grid stays upright, with no recession toward the horizon. The minimum versions come from the inset property on the star layer: with top: 0; right: 0; bottom: 0; left: 0 instead, the effect works in much older browsers.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0050-root">
<div class="xr-environment" style="width:100%;min-height:300px;">
<div class="xr-env-stars"></div> <!-- 40 stars added by the script -->
<div class="xr-env-obj" style="left: 30%; background: linear-gradient(135deg, #6366f1, #a855f7); animation-delay: 0s;"></div>
<div class="xr-env-obj" style="left: 60%; background: linear-gradient(135deg, #ec4899, #f43f5e); animation-delay: 1s;"></div>
<div class="xr-env-ground">
<div class="xr-env-grid-floor"></div>
</div>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Number of stars (JS — i < 40) |
40 per backdrop | 80 gives a dense sky, 15 a city sky. Beyond 200, mind the cost: each star is an animation. |
Star area (JS — Math.random() * 60 + '%' for top) |
the top 60% | Keep it above the horizon (60% as long as the floor takes 40%), otherwise stars land on the floor. |
Twinkle pace (JS — 1.5 + Math.random() * 2 and Math.random() * 3) |
durations of 1.5 to 3.5 s, starts of 0 to 3 s | Durations of 3 to 6 s give a calmer sky; tightened around 1 s, it crackles. Keep a random start: synchronized stars would blink together. |
Floating objects (HTML — inline style of each .xr-env-obj) |
2 squares of 30 px, at 30% and 60%, purple and pink | Change left, background and animation-delay in the inline style; add more by copying the line. For an offset from the first frame, prefer a negative delay (-1.5s). |
Floor (CSS — .xr-env-ground 40%, perspective: 300px, rotateX(60deg), 30 px grid) |
40% of the height, 60°, 10% indigo lines | A shorter perspective (200 px) and 70° give a floor that recedes more; more opaque, colored lines (50 to 75%) give the neon look of scene 2. |
Sky (CSS — background on .xr-environment) |
#0a0a2e → #1a1a3e → #2a2a4e | Any vertical gradient works; keep the top dark so the white stars stay visible. |
FAQ
Math.random(): position, opacity, twinkle duration and start. For the same sky on every visit, replace Math.random() with a small seeded generator (a sequence of numbers that is always the same), or write the stars one by one in the HTML.