The Solitaire victory animation as a drop-in canvas plugin. The classic trail-smearing bounce plus scatter, rain, fountain, and cyclone effects — 52 procedurally-drawn cards, zero dependencies, no build step.
Anyone who ever won a game of Solitaire on Windows remembers the payoff:
cards launching off their foundations, bouncing across the felt, smearing
trails behind them because the screen never repainted. solitaire-victory.js recreates
that — faithfully — and then keeps going with four more ways to shower a
screen in playing cards.
npm install @goboldlyforward/solitaire-victoryOr grab the files directly:
<link rel="stylesheet" href="path/to/solitaire-victory.css">
<script src="path/to/solitaire-victory.js"></script>Works as a plain global (SolitaireVictory) or a CommonJS module.
<div id="stage" style="height: 60vh"></div>
<script>
const win = new SolitaireVictory('#stage');
win.play('bounce').then(() => {
console.log('all 52 cards are down');
});
</script>Or skip the container entirely — the one-liner throws a fullscreen overlay over your whole app, plays once, fades out, and removes itself. Clicks pass straight through the entire time:
SolitaireVictory.celebrate(); // classic bounce
SolitaireVictory.celebrate({ effect: 'scatter', holdMs: 500 }); // any effect + options| Effect | What happens |
|---|---|
bounce |
The 1990 classic. Cards launch one-by-one from foundation piles, bounce off the floor, and smear trails because the canvas never repaints. |
scatter |
The whole deck blasts outward from the center and tumbles off-screen. |
rain |
Cards fall from the sky, tumbling, a mix of faces and backs. With loop: true they recycle seamlessly forever. |
fountain |
A geyser of cards from the bottom center — confetti-cannon energy. |
cyclone |
Cards spiral out from the center in a widening vortex. |
Every card face — pips, courts, and the patterned back — is drawn programmatically on canvas and cached as a sprite, so cards stay crisp at any size and any devicePixelRatio.
new SolitaireVictory('#stage', {
effect: 'bounce', // 'bounce' | 'scatter' | 'rain' | 'fountain' | 'cyclone'
cards: 52, // deck size the effect animates
cardWidth: 72, // px; height keeps the 2.5 × 3.5 poker ratio
speed: 1, // global time multiplier
gravity: 2200, // px/s²
restitution: 0.82, // bounce: energy kept per floor hit
stagger: 0.4, // bounce: seconds between card launches
piles: 4, // bounce: foundation piles across the top
loop: false, // replay forever (rain recycles seamlessly)
trails: null, // null = effect default (bounce smears, others don't)
background: null, // canvas backdrop; null = transparent
backColor: '#2b4d9b', // card-back color
faceDownRatio: null, // fraction of face-down cards (rain 0.35, cyclone 0.25)
onComplete: null, // fn({ effect, willLoop }) — fires every completion
autoplay: false, // play() immediately on construct
});With background: null (the default) the canvas is transparent, so your
own CSS — a green felt gradient, an image, anything — shows through, and
bounce trails smear right over it.
win.play(effect?, overrides?); // returns a Promise: 'complete' | 'stopped'
win.pause();
win.resume();
win.stop(); // halt + wipe the canvas (trails included)
win.clear(); // wipe trails without ending the run
win.setOptions({ ... }); // merge + apply; speed/gravity change live
win.resize(); // manual re-measure (auto via ResizeObserver)
win.destroy(); // tear down canvas + listeners
win.isPlaying; // boolean
SolitaireVictory.EFFECTS; // ['bounce', 'scatter', 'rain', 'fountain', 'cyclone']play() interrupts any run already in progress. setOptions() applies
speed, gravity, restitution, and stagger live mid-run; changing
effect, cards, cardWidth, or the deck's look restarts the current run.
The bounce effect never clears the canvas — that's the whole trick, exactly
like the original. trails is overridable per effect:
win.play('rain', { trails: true }); // rain that smears — surprisingly great
win.play('bounce', { trails: false }); // a tidy, repainting bounce- An element (
new SolitaireVictory('#stage')) — a canvas is injected and fills it; anything else inside the host auto-stacks above the cards. - A
<canvas>— painted directly, no wrapper. - Nothing /
document.body(new SolitaireVictory()) — a fixed, fullscreen, click-through overlay is created for you (that's whatSolitaireVictory.celebrate()uses).