360 Viewer
A strip three times wider than its frame, painted with a long gradient from midnight blue to yellow and back to night, that you slide with a mouse, a finger or the arrow keys, and that loops endlessly: the script copies it end to end. A horizontal panorama you unroll — all the way around for a 360° panoramic photo, with no looking up or down.
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-0042-root (#0a0a0f background, at least 300 px tall) holds the .xr-360-viewer frame (full width × 300 px, 8 px rounded corners, overflow: hidden) and the .xr-360-scene strip, placed at the top left and 300% wide: the frame shows a third of it. It is painted with an eight-step horizontal gradient, from #1a1a2e to blue #0f3460, purple #533483, red #e94560, peach #f38181 and yellow #fce38a, then #1a1a2e again: the strip ends on the exact color it starts with, which is what makes the loop invisible.
On startup, the script clones the strip, marks the copy aria-hidden="true" and glues it to its left. It keeps a single number, the total offset, and places the two strips at p and p − width, where p is the offset brought back between 0 and the strip's width: whatever distance has been dragged, the frame is always covered. Measured in Chromium 151, Firefox 153 and WebKit 26: after drags of 400 px to the right then 1,500 and 3,000 px to the left, not a single empty pixel column in the frame. The drag follows the pointer to the pixel (120 px of dragging = 120 px of strip), with no transition and no inertia; a full turn takes three frame widths.
Pointer events serve mouse, finger and pen alike, with pointer capture during the gesture; touch-action: pan-y lets the page scroll vertically when the finger moves up or down, and keeps the horizontal gesture for the panorama (measured with a finger in Chromium using real touch events). With the keyboard, the frame takes focus and each left or right arrow turns by a tenth of the frame's width. One instance starts per frame found under a .fx-0042-root root: two panoramas on one page each slide on their own (measured). It is not a sphere: you turn your head left and right, never up or down, and the image is shown flat, with no projection correction.
Accessibility
- Keyboard: the frame is focusable (
tabindex="0"), announced by itsaria-label(a panorama to drag or turn with the arrows), and gets a 2 px yellow outline on focus. Left or right arrow: a tenth of the frame's width per press. Measured in Chromium, Firefox and WebKit. - Reduced motion: nothing moves on its own and no transition animates the strip: it only moves under the finger, the mouse or the key, at the user's pace. Rendering is therefore identical with or without
prefers-reduced-motion(measured: 0 animations in both cases). - Strip content: the loop's copy is hidden from screen readers, so markers placed in the strip are read only once. A background image has no text alternative: if the panorama carries information (peaks, times, steps), also write it as text, in the strip or next to it, as the scenes above do.
- Touch:
touch-action: pan-ykeeps the page's vertical scrolling; only a horizontal drag turns the panorama.
Browser compatibility
CSS (2D transforms, gradient) + 41 lines of JavaScript (strip cloning, pointer events with capture, keyboard, resize), no dependencies. Measured in Chromium 151, Firefox 153 and WebKit 26: loop with no empty band after 3,000 px of dragging, pixel-accurate drag, keyboard, two independent copies; touch measured in Chromium. Nothing is 3D: WebKit's rendering is representative here.
Without JavaScript, the frame shows the first third of the strip, still, and nothing slides. Without pointer events (very old browsers), same thing: the panorama stays readable but frozen. If your image's left and right edges do not match, the loop still works, but the seam shows on every turn.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0042-root">
<div class="xr-360-viewer" tabindex="0" role="group" aria-label="Panorama: drag, or use the left and right arrow keys, to look around" style="width:100%;height:300px;">
<div class="xr-360-scene">
<!-- your panorama: gradient, background image, absolutely positioned markers -->
</div>
<!-- the script adds a copy of .xr-360-scene here (aria-hidden) for the loop -->
</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 |
|---|---|---|
Panoramic image (CSS — background on .xr-360-scene) |
eight-step gradient, #1a1a2e at both ends | Replace the gradient with url(your-panorama.jpg) center / 100% 100%. For a seamless loop, the image's right edge must continue its left edge: a 360° panoramic photo does. For a gradient, end on the starting color. |
Strip length (CSS — width: 300%) |
300% (one turn = three frame widths) | Match it to your image's ratio: a 6,000 × 1,000 px panorama in a 300 px tall frame is 1,800 px long, i.e. 300% of a 600 px frame. The longer the strip, the more dragging a full turn takes. |
Frame height (HTML — style="height:300px" on the frame) |
300 px | The style is written in the HTML: change it there. If you increase the height, keep the strip's length proportional so the image is not distorted. |
Keyboard step (JS — viewer.offsetWidth / 10) |
a tenth of the frame's width | / 20 gives finer steps; / 4 jumps a quarter of the frame, handy to go from one marker to the next. |
Markers in the strip (HTML — position: absolute elements inside .xr-360-scene) |
none | Place them as a percentage of the strip (left: 32%): they turn with the panorama and the loop's copy repeats them automatically. Do not give them an id: the copy would duplicate it. |
Background and outline (CSS — .fx-0042-root, the frame's border-radius and :focus-visible) |
#0a0a0f, 8 px corners, yellow #fce38a outline | Remove the root's background to set the frame on your page; match the focus outline color to your brand while keeping a clear contrast with the panorama. |
FAQ
aria-hidden so screen readers do not read the markers twice; avoid id attributes inside the strip, they would be duplicated.touch-action: pan-y leaves vertical gestures to the browser, so the page scrolls when the finger moves up or down; only a horizontal gesture turns the panorama. The script uses pointer events, shared by mouse, finger and pen (measured with a finger in Chromium using real touch events).