Declarative scroll animation library for Svelte 5 + GSAP. Pin scenes, animate elements in/out, reveal text, add parallax — all composable.
Built by Aki Sato. Powered by GSAP and SplitType.
# 1. install gscene
npm install github:AkiSato49/gscene
# 2. install peer deps
npm install gsap split-type
// vite.config.ts
import { resolve } from 'path';
export default defineConfig({
plugins: [tailwindcss(), sveltekit()],
resolve: {
alias: {
'gscene': resolve('./node_modules/gscene/src/index.ts')
}
}
});
{
"compilerOptions": {
"paths": {
"gscene": ["./node_modules/gscene/src/index.ts"]
}
}
}
<script>
import { Scene, SceneContainer, SceneItem, slideIn, slideOut, fadeOut } from 'gscene';
</script>
<Scene
in={slideIn({ direction: 'right', stagger: 0.15 })}
out={slideOut({ direction: 'left', stagger: 0.15 })}
>
<SceneContainer class="grid grid-cols-3 gap-4 p-16">
<div>Animates with Scene default</div>
<div>Animates with Scene default</div>
<!-- override animation for this item -->
<SceneItem out={fadeOut()}>
<div>Fades out instead</div>
</SceneItem>
</SceneContainer>
</Scene>
| Component | Description |
|---|---|
<Scene> |
Pins a section and runs in/out animations on scroll |
<SceneContainer> |
Marks the container whose children get animated |
<SceneItem> |
Overrides animation for a specific child |
<HorizontalScroll> |
Horizontal scroll driven by vertical scroll |
<TextReveal> |
Split text animations (lines, words, chars) |
<ParallaxLayer> |
Scroll-driven parallax |
<CountUp> |
Animated number counting on scroll |
<Scene>Pins a full-screen section. Elements animate in, hold, then animate out as you scroll.
| Prop | Type | Default | Description |
|---|---|---|---|
in |
AnimFn |
— | Animation on enter |
out |
AnimFn |
— | Animation on exit |
inPosition |
string |
'0' |
Timeline position for in animation |
holdDuration |
number |
1 |
Seconds to hold between in and out |
scrub |
number |
1 |
Scroll smoothing (1 = 1s lag) |
scrollDistance |
number |
300 |
Scroll distance multiplier |
reverse |
boolean |
false |
Reverse animation on scroll up |
snap |
boolean | number |
false |
Snap to edges |
class |
string |
— | Extra CSS class |
<SceneContainer>Marks the container. All direct children are auto-animated with Scene's default.
<Scene out={slideOut({ stagger: 0.15 })}>
<SceneContainer class="flex gap-8 p-16">
<div>item 1</div>
<div>item 2</div>
<div>item 3</div>
</SceneContainer>
</Scene>
<SceneItem>Overrides the Scene default for one specific child. Uses display: contents — layout unaffected.
<SceneItem out={fadeOut()} in={slideIn({ direction: 'up' })}>
<div>this one is different</div>
</SceneItem>
slideIn / slideOutslideIn({ direction?: 'left'|'right'|'up'|'down', angle?: number, distance?: string, stagger?: number, ease?: string, duration?: number, speed?: number, overlap?: number })
slideOut({ ...same })
angle overrides direction when set. 0 = right, 90 = down, 45 = diagonal, etc.
fadeIn / fadeOutfadeIn({ scale?: number, stagger?: number, ease?: string, duration?: number, speed?: number })
fadeOut({ ...same })
scaleIn / scaleOutscaleIn({ scale?: number, opacity?: number, stagger?: number })
scaleOut({ ...same })
rotateIn / rotateOutrotateIn({ deg?: number, opacity?: number, stagger?: number })
rotateOut({ ...same })
spinIn / spinOutspinIn({ turns?: number, direction?: 'cw'|'ccw', fadeOut?: boolean, stagger?: number })
spinOut({ ...same })
flipIn / flipOutflipIn({ axis?: 'x'|'y', fadeOut?: boolean, stagger?: number })
flipOut({ ...same })
spiralIn / spiralOutspiralIn({ turns?: number, distance?: string, direction?: 'cw'|'ccw', stagger?: number })
spiralOut({ ...same })
shakeshake({ intensity?: number, stagger?: number, duration?: number })
swingswing({ deg?: number, origin?: string, stagger?: number })
combineRun multiple animations simultaneously:
combine(
slideOut({ direction: 'right' }),
fadeOut()
)
| Option | Default | Description |
|---|---|---|
ease |
varies | GSAP ease string |
duration |
1 |
Duration in seconds |
delay |
0 |
Delay before start |
stagger |
0 |
Delay between each element |
speed |
— | Duration multiplier (2 = 2× faster) |
overlap |
— | Overlap with previous (<0 = overlap, >0 = gap) |
<HorizontalScroll><HorizontalScroll direction="left" scrub={1.5}>
<section class="flex h-dvh items-center justify-center">One</section>
<section class="flex h-dvh items-center justify-center">Two</section>
</HorizontalScroll>
| Prop | Type | Default | Description |
|---|---|---|---|
direction |
'left'|'right' |
'left' |
Scroll direction |
scrub |
number |
1 |
Scroll smoothing |
ease |
string |
'none' |
GSAP ease |
<TextReveal><TextReveal by="words" type="clip" stagger={0.05}>
<h1 class="text-8xl">Hello World</h1>
</TextReveal>
| Prop | Type | Default | Description |
|---|---|---|---|
by |
'lines'|'words'|'chars' |
'lines' |
How to split text |
type |
'slide'|'fade'|'scale'|'rotate'|'wave'|'blur'|'clip' |
'slide' |
Animation type |
direction |
'up'|'down'|'left'|'right' |
'up' |
Slide direction |
stagger |
number |
0.05 |
Delay between units |
duration |
number |
0.8 |
Duration |
ease |
string |
'power3.out' |
GSAP ease |
distance |
string |
'100%' |
Slide distance |
scrub |
boolean|number |
false |
Tie to scroll |
start |
string |
'top 80%' |
ScrollTrigger start |
<ParallaxLayer><ParallaxLayer speed={0.3}>
<img src="/bg.jpg" class="w-full" />
</ParallaxLayer>
| Prop | Type | Default | Description |
|---|---|---|---|
speed |
number |
0.5 |
0 = still, 1 = scroll speed, -1 = opposite |
direction |
'y'|'x' |
'y' |
Parallax axis |
scrub |
boolean|number |
true |
Smoothing |
<CountUp><CountUp from={0} to={1000} suffix="+" prefix="$" />
| Prop | Type | Default | Description |
|---|---|---|---|
to |
number |
required | Target number |
from |
number |
0 |
Starting number |
duration |
number |
1.5 |
Duration |
decimals |
number |
0 |
Decimal places |
prefix |
string |
'' |
Text before |
suffix |
string |
'' |
Text after |
separator |
string |
',' |
Thousands separator |
scrub |
boolean|number |
false |
Tie to scroll |
start |
string |
'top 80%' |
ScrollTrigger start |
All animations automatically disable when prefers-reduced-motion is set in the OS.
All types exported:
import type { AnimFn, SceneProps, SlideOptions, TextRevealProps } from 'gscene';
<DirectionalScroll>Scroll panels in any direction — horizontal, vertical, diagonal, or any angle.
<DirectionalScroll angle={0} scrub={1.5}>
<section class="flex h-dvh items-center justify-center">One</section>
<section class="flex h-dvh items-center justify-center">Two</section>
<section class="flex h-dvh items-center justify-center">Three</section>
</DirectionalScroll>
| Prop | Type | Default | Description |
|---|---|---|---|
angle |
number |
0 |
Direction in degrees (0 = right, 90 = down, 45 = diagonal) |
scrub |
number |
1 |
Scroll smoothing |
ease |
string |
'none' |
GSAP ease |
| Angle | Direction |
|---|---|
0 |
→ right (horizontal) |
90 |
↓ down (vertical) |
180 |
← left |
270 |
↑ up |
45 |
↘ diagonal |
-45 |
↗ diagonal |
MIT © Aki Sato