Everything ease360° needs is an ordered image sequence and three options. This example goes from install to a working viewer — no loader, no callbacks, just the minimum viable setup.
In this example you will learn the following:
frames is the only required option — sourceWidth and sourceHeight are optionalsourceWidth/sourceHeight explicitly for faster synchronous init and CORS safetyease360° ships as a UMD bundle — it works in any environment without a build step. Pick the approach that fits your stack.
<!-- Script tag — no build tools required -->
<script src="lib/ease360-1.0.0.min.js"></script>
<link rel="stylesheet" href="lib/ease360.css">
# npm
npm install ease360
// ESM
import ease360 from 'ease360';
// CommonJS
const ease360 = require('ease360');
Always include ease360.css alongside the script — it sets the
grab cursor on the viewer so users immediately know it's interactive.
Without it the cursor stays as the default arrow and the affordance is lost.
ease360° injects a <canvas> element into whichever container
you point it at. Give the container a size in CSS — the canvas fills it.
You don't create the canvas yourself.
<div id="myEase360"></div>
#myEase360 {
width: 100%;
height: 480px;
}
ease360° needs an ordered array of image paths — one per angle step, covering a full 360° rotation. The frame count must divide evenly into 360: 18 frames (every 20°) is lightweight with visible stepping at slow speeds; 36 frames (every 10°) is the sweet spot for most product work; 72 frames (every 5°) is silky at any speed with a heavier payload.
The teapot used here has 36 frames shot every 10°, named sequentially:
teapot_0.jpg through teapot_35.jpg.
That naming convention — a zero-based index stepping by 1 — is the most
portable: easy to generate, easy to validate, no ambiguity about order.
// Build the sequence from a local image folder.
// Array index = frame order. ease360° renders them in this exact sequence.
const imgPath = 'images/teapot_green_36/';
const greenTeapot = Array.from(
{ length: 36 },
(_, i) => `${imgPath}teapot_${i}.jpg`
);
// For CDN-served assets (recommended for production) see Example 5 — CDN & Cloudinary,
// or pass any valid URL array here directly.
ease360(selector, options) — first argument is a CSS selector
or DOM element. Only one option is truly required: frames.
sourceWidth and sourceHeight are optional — see
the next section for when to provide them and why it matters.
const myEase360 = ease360('#myEase360', {
frames: greenTeapot, // required — ordered array of image paths
sourceWidth: 540, // optional — see below for the trade-offs
sourceHeight: 540, // optional — omit to let ease360° detect automatically
backgroundSize: 'cover' // fill the container, preserve aspect ratio
});
That's it — drag to spin, physics damping on release. The canvas fades in automatically once the first frame loads. For loading indicators, callbacks, and controls see Example 2 — Callbacks & Control.
Tell ease360° the pixel dimensions of the delivered image — what the browser
actually receives, not the master file. These drive all canvas math: scaling,
aspect ratio, cover positioning, and crop offsets.
They are optional. When omitted, ease360° probes frames[0]
automatically. Provide them to skip the probe for synchronous init, no CORS
dependency, and no async delay (typically 50–200ms on a CDN).
// Probe — convenient, small async delay, requires CORS headers on your server
ease360('#myEase360', { frames: greenTeapot, backgroundSize: 'cover' });
// Explicit — synchronous init, no probe, no CORS dependency
ease360('#myEase360', {
frames: greenTeapot,
sourceWidth: 540, // pixel width the browser receives — not the master
sourceHeight: 540, // must match the delivered size, not the original upload
backgroundSize: 'cover'
});
For CDN assets with URL-based resizing, see
Example 5 — CDN & Cloudinary for the full
pattern. If anything goes wrong — black canvas, nothing rendering —
open the console. ease360° logs a [ease360] message for
every failure including CORS blocks and probe failures.
See Common Errors & Debugging.