Quick start
Drop a container in the DOM, hand the slideshow a list of slide sources and a transition, and you have a working carousel with click halves, keyboard nav, autoplay, and pause-on-hidden wired by default.
import { createSlideshow } from "@vysmo/slideshow";
import { paintBleed } from "@vysmo/transitions";
const container = document.querySelector<HTMLDivElement>("#stage")!;
const show = createSlideshow({
container,
slides: [
"/photo-01.jpg",
"/photo-02.jpg",
"/photo-03.jpg",
],
transition: paintBleed,
transitionDuration: 900,
autoplayDelay: 4000,
loop: true,
});
await show.ready;
show.next();
show.go(2);
show.on("change", (current) => console.log("now showing", current));
Three pieces. The container holds the canvas —
size it via CSS. Slide sources are URL strings
(decoded to images) or DOM elements you've already prepared
(use a canvas if you need rich content: pre-render with
html2canvas / OffscreenCanvas, or paint video frames via
drawImage). The handle is your
imperative API: next, prev, go,
play / pause, plus event subscriptions.
All built-in interactions can be disabled per-option (see Recipes).
Auto-rotating hero, pause on hover
A landing-page slideshow that advances on its own, pauses on
hover, and shows arrows + dots + a countdown progress bar. The
pauseOnHover + progress combination is
the autoplay UX users expect from polished carousels.
import { createSlideshow } from "@vysmo/slideshow";
import { lightLeak } from "@vysmo/transitions";
const show = createSlideshow({
container,
slides: heroPhotos,
transition: lightLeak,
autoplayDelay: 5000,
loop: true,
pauseOnHover: true,
arrows: true,
dots: true,
progress: true,
});
Tune every visible piece
Each chrome option accepts true (sensible defaults),
false (off), or an options object for fine-tuning.
Mix freely — the slideshow renders only the pieces you opt in.
import { createSlideshow } from "@vysmo/slideshow";
import { paintBleed } from "@vysmo/transitions";
createSlideshow({
container,
slides: photos,
transition: paintBleed,
arrows: { position: "outside-edges", style: "circle" },
dots: { position: "bottom-center", style: "lines" },
counter: { position: "top-right",
format: (i, n) => `${String(i+1).padStart(2,"0")} / ${n}` },
progress: { position: "top" },
captions: {
texts: photos.map((p) => p.alt),
position: "bottom",
alignment: "left",
},
swipeNavigation: { threshold: 60 },
});
Theme via CSS custom properties
Chrome inline styles read --vysmo-chrome-*
variables with fallbacks. Override on any ancestor of the
slideshow container to retheme without forking.
:root {
--vysmo-chrome-color: white;
--vysmo-chrome-bg: rgba(0, 0, 0, 0.42);
--vysmo-chrome-bg-strong: rgba(0, 0, 0, 0.62);
--vysmo-chrome-active: white;
--vysmo-chrome-inactive: rgba(255, 255, 255, 0.5);
--vysmo-chrome-shadow: drop-shadow(0 1px 2px rgba(0,0,0,0.45));
}
.brand-hero {
--vysmo-chrome-active: oklch(70% 0.18 280);
--vysmo-chrome-bg: rgba(20, 8, 30, 0.55);
}
Per-slide transition variety
Round-robin through a curated pool so each slide change uses a
different transition. The function form of transition
gets called fresh on every change.
import { createSlideshow } from "@vysmo/slideshow";
import {
paintBleed,
lightLeak,
pageCurl,
swirl,
} from "@vysmo/transitions";
const pool = [paintBleed, lightLeak, pageCurl, swirl];
createSlideshow({
container,
slides: photos,
transition: (from, to) => pool[to % pool.length]!,
autoplayDelay: 4500,
});
Custom controls + dot indicator
Disable the built-in click / keyboard nav, build your own
prev / next / dot UI, and wire it through the handle.
const show = createSlideshow({
container,
slides: photos,
clickNavigation: false,
keyboardNavigation: false,
});
document.querySelector("#prev")!.addEventListener("click", () => show.prev());
document.querySelector("#next")!.addEventListener("click", () => show.next());
document.querySelectorAll<HTMLButtonElement>("[data-go]").forEach((btn) => {
btn.addEventListener("click", () => show.go(Number(btn.dataset.go)));
});
show.on("change", (current) => {
document.querySelector("#counter")!.textContent =
`${current + 1} / ${show.length}`;
});
Scroll-driven slideshow
Tie slide changes to scroll position. Each quarter of the scroll
zone moves to the next slide; prefers-reduced-motion
users will appreciate instant: true on the
go() call.
import { createSlideshow } from "@vysmo/slideshow";
import { createScrollProgress } from "@vysmo/scroll";
const show = createSlideshow({
container,
slides: photos,
autoplayDelay: 0,
clickNavigation: false,
});
let lastIndex = 0;
createScrollProgress({
target: container,
onProgress: (p) => {
const target = Math.floor(p * show.length);
if (target !== lastIndex && target < show.length) {
lastIndex = target;
void show.go(target, { instant: false });
}
},
});