// luna_motion.archive

코드 애니메이션 아카이브

126가지 모션 효과를 자체 모션 엔진(Luna Motion v1.0)으로 구현한 라이브 데모 · 코드 샘플 모음. 이징·스태거·타임라인·스프링·SVG·드래그·파티클·로더·게임·물리·트랜지션·글리치·차트·마스코트·UI 알림·오디오·페이퍼까지 — 외부 라이브러리 없이 순수 JS 한 파일로 동작. 각 카드의 "코드 보기" 버튼으로 바로 복사해 다른 도구에 적용할 수 있습니다.

엔진 크기 ~15 KB 의존성 0개 이징 함수 27종 데모 126개 저작권 Luna Whale 자체 구현
Layout & Position 요소 배치 모션
01 · Stagger Grid Pop
Layout
// 격자가 중심에서 바깥으로 차례로 등장
const grid = document.querySelectorAll('#grid1 > div');
lm.motion({
  targets: grid,
  scale:   [0, 1],
  opacity: [0, 1],
  duration: 700,
  delay:   lm.stagger(40, { grid: [8, 4], from: 'center' }),
  easing:  'outBack',
});
02 · Wave Translate
Layout
// 막대들이 사인파처럼 위아래 출렁
lm.motion({
  targets: '#wave2 > div',
  translateY: [0, -40],
  duration: 800,
  delay:   lm.stagger(60, { from: 'center' }),
  loop:    true,
  direction: 'alternate',
  easing:  'ioSine',
});
03 · Orbit System
Layout
// 원 궤도 회전 — translateX 후 rotate
lm.motion({
  targets: '#planet3a',
  rotate:  [0, 360],
  translateX: 60,
  duration: 3000,
  loop: true,
  easing: 'linear',
});
04 · Magnetic Pull
Inter
// 마우스 따라 부드럽게 끌려옴
const el = document.querySelector('#magnet4');
wrap.addEventListener('pointermove', e => {
  const r = wrap.getBoundingClientRect();
  const dx = e.clientX - r.left - r.width/2;
  const dy = e.clientY - r.top - r.height/2;
  lm.motion({
    targets: el,
    translateX: dx*0.3, translateY: dy*0.3,
    duration: 400, easing: 'outQuart',
  });
});
05 · Bounce In
Layout
// 위에서 떨어져 통통 튀며 등장
lm.motion({
  targets: '#bounce5 > div',
  translateY: [-200, 0],
  opacity: [0, 1],
  duration: 1200,
  delay:   lm.stagger(100),
  easing:  'outBounce',
});
Text Effects 글자·단어 애니메이션
Luna Whale
06 · Letter Reveal
Text
// 글자별로 분할 후 위에서 떨어짐
const spans = lm.splitText($('#letter6'));
lm.motion({
  targets: spans,
  translateY: [-30, 0],
  opacity: [0, 1],
  rotate: [-15, 0],
  duration: 800,
  delay: lm.stagger(60),
  easing: 'outBack',
});
코드 한 줄로 단어가 차례차례 풀려나옵니다
07 · Word Stagger
Text
// 단어 단위로 분할
const words = lm.splitText($('#word7'), { by: 'word' });
lm.motion({
  targets: words,
  translateY: [14, 0],
  opacity: [0, 1],
  duration: 600,
  delay: lm.stagger(90),
});
08 · Typewriter
Text
// 한 글자씩 타자치는 효과
const text = "> init luna.whale.tech";
let i = 0;
const timer = setInterval(() => {
  el.textContent = text.slice(0, ++i);
  if (i >= text.length) clearInterval(timer);
}, 80);
DECRYPT_CODE
09 · Scramble Decrypt
Text
// 무작위 글자가 차례로 정답으로 복원
function scramble(el, target) {
  const chars = "!@#$%^&*()_+ABC0123";
  let step = 0;
  const id = setInterval(() => {
    el.textContent = target.split('').map((c, i) =>
      i < step ? c : chars[Math.floor(Math.random() * chars.length)]
    ).join('');
    step++;
    if (step > target.length) clearInterval(id);
  }, 60);
}
WAVE TEXT EFFECT
10 · Text Wave
Text
// 글자가 위아래로 영구 출렁
const spans = lm.splitText($('#textwave10'));
lm.motion({
  targets: spans,
  translateY: [0, -12],
  duration: 800,
  delay: lm.stagger(70, { from: 'center' }),
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
});
SVG & Path 벡터 모션
11 · SVG Stroke Draw
SVG
// 패스를 한 번에 그리는 효과
const path = $('#path11');
const len = lm.pathLength(path);
path.setAttribute('stroke-dasharray', len);
lm.motion({
  targets: path,
  strokeDashoffset: [len, 0],
  duration: 1500,
  easing: 'ioCubic',
});
12 · Polygon Morph
SVG
// 점좌표 보간으로 다각형 모핑
const A = [[100,30],[170,80],[145,160],[55,160],[30,80]];
const B = [[100,100],[100,100],[100,100],[100,100],[100,100]];
tween(0, 1, t => {
  poly.setAttribute('points',
    A.map(([x,y],i) => (x+(B[i][0]-x)*t)+','+(y+(B[i][1]-y)*t))
     .join(' '));
});
13 · Logo Reveal
SVG
// 여러 path를 timeline 으로 순서 있게 그림
const tl = lm.timeline();
parts.forEach((p, i) => {
  const len = lm.pathLength(p) || 120;
  p.setAttribute('stroke-dasharray', len);
  tl.add({
    targets: p,
    strokeDashoffset: [len, 0],
    duration: 700,
  }, i*200);
});
tl.play();
14 · Sine Wave
SVG
// 사인파 d 속성 보간
lm.motion({
  targets: { phase: 0 },
  phase: 2*Math.PI,
  duration: 2000,
  loop: true,
  easing: 'linear',
  update: t => {
    const d = buildSineD(t * 2*Math.PI);
    path.setAttribute('d', d);
  },
});
15 · Path Follow
SVG
// 점이 패스를 따라 움직임 (getPointAtLength)
const path = $('#path15');
const len = path.getTotalLength();
lm.motion({
  targets: { p: 0 },
  p: 1,
  duration: 2500,
  loop: true,
  easing: 'ioSine',
  update: t => {
    const pt = path.getPointAtLength(len*t);
    follower.style.transform = `translate(${pt.x}px, ${pt.y}px)`;
  },
});
Color & Filter 색상·필터 모션
16 · Hue Cycle
Color
// hue-rotate 필터로 색상환 회전
lm.motion({
  targets: el,
  filter: ['hue-rotate(0deg)', 'hue-rotate(360deg)'],
  duration: 3000,
  loop: true,
  easing: 'linear',
});
17 · Glow Pulse
Color
// box-shadow + scale 무한 펄스
lm.motion({
  targets: el,
  scale: [1, 1.3],
  boxShadow: ['0 0 0px #06b6d4', '0 0 60px #ec4899'],
  duration: 800,
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
});
18 · Color Stagger
Color
// 회색 → 무지개로 차례차례 색칠
const palette = ['#ef4444','#f97316','#fbbf24','#10b981','#06b6d4','#8b5cf6','#ec4899'];
dots.forEach((d, i) => {
  lm.motion({
    targets: d,
    backgroundColor: ['#64748b', palette[i]],
    scale: [0.7, 1.1, 1],
    duration: 600,
    delay: i*100,
    easing: 'outBack',
  });
});
19 · Gradient Shift
Color
// background-position 으로 그라디언트 이동
lm.motion({
  targets: { p: 0 },
  p: 100,
  duration: 3000,
  loop: true,
  easing: 'linear',
  update: t => el.style.backgroundPosition = (t*400) + '% 0',
});
UNBLUR ME
20 · Blur Reveal
Color
// blur 12px → 0 풀림
lm.motion({
  targets: { b: 12 },
  b: 0,
  duration: 1200,
  easing: 'outQuart',
  update: t => {
    const b = 12 - t*12;
    el.style.filter = `blur(${b}px)`;
    el.style.opacity = t;
  },
});
3D & Transform 입체 변환
A
B
21 · 3D Card Flip
3D
// rotateY 0 → 180 (preserve-3d 필요)
lm.motion({
  targets: card,
  rotateY: [0, 180],
  duration: 1000,
  loop: true,
  direction: 'alternate',
  easing: 'ioCubic',
});
F
B
R
L
T
D
22 · Cube Rotate
3D
// rotateX + rotateY 무한 회전
lm.motion({
  targets: cube,
  rotateX: [0, 360],
  rotateY: [0, 360],
  duration: 5000,
  loop: true,
  easing: 'linear',
});
3D
23 · Tilt Hover
Inter
// 마우스 위치에 따라 perspective 기울이기
wrap.addEventListener('pointermove', e => {
  const r = wrap.getBoundingClientRect();
  const rx = ((e.clientY-r.top)/r.height-.5) * -30;
  const ry = ((e.clientX-r.left)/r.width-.5) * 30;
  card.style.transform = `rotateX(${rx}deg) rotateY(${ry}deg)`;
});
24 · Card Stack
3D
// 적층 카드가 펼쳐짐
cards.forEach((c, i) => {
  lm.motion({
    targets: c,
    translateX: [0, (i-1.5)*60],
    translateY: [0, 0],
    rotate: [0, (i-1.5)*8],
    duration: 800,
    delay: i*80,
    easing: 'outBack',
  });
});
☁️
⛰️
🌳
🐰
25 · Parallax Mouse
Inter
// 깊이에 따라 다른 속도로 이동 (data-depth 활용)
wrap.addEventListener('pointermove', e => {
  layers.forEach(L => {
    const d = +L.dataset.depth;
    L.style.transform = `translate(${dx*d}px, ${dy*d}px)`;
  });
});
Interactive 드래그·스크롤·물리
DRAG
26 · Drag & Snap
Inter
// 드래그 후 가장 가까운 zone으로 spring snap
let dragging = false, sx, sy, x = 0, y = 0;
handle.addEventListener('pointerdown', e => {
  dragging = true;
  sx = e.clientX - x; sy = e.clientY - y;
});
addEventListener('pointerup', () => {
  if (!dragging) return;
  dragging = false;
  const snap = findNearestZone(x, y);
  lm.motion({
    targets: handle,
    translateX: snap.x, translateY: snap.y,
    duration: 700,
    easing: lm.spring({ stiffness: 120, damping: 10 }),
  });
});
CLICK ANYWHERE
27 · Click Ripple
Inter
// 클릭 위치에서 원이 퍼져나감
stage.addEventListener('click', e => {
  const r = stage.getBoundingClientRect();
  const rip = document.createElement('div');
  rip.className = 'ripple';
  rip.style.left = (e.clientX - r.left) + 'px';
  rip.style.top  = (e.clientY - r.top) + 'px';
  stage.appendChild(rip);
  lm.motion({
    targets: rip,
    width: ['0px', '200px'],
    height: ['0px', '200px'],
    opacity: [1, 0],
    duration: 800,
    complete: () => rip.remove(),
  });
});
📦 Item 01 · 스크롤 등장
📦 Item 02 · 보일 때마다 fade-up
📦 Item 03 · IntersectionObserver
📦 Item 04 · 자체 모션 엔진 사용
📦 Item 05 · 무한 리스트 가능
📦 Item 06 · 스태거도 합성 가능
📦 Item 07 · 끝
28 · Scroll Reveal
Inter
// IntersectionObserver + lm.motion
const io = new IntersectionObserver(es => {
  es.forEach(e => {
    if (e.isIntersecting) {
      lm.motion({
        targets: e.target,
        translateY: [20, 0],
        opacity: [0, 1],
        duration: 500,
      });
      io.unobserve(e.target);
    }
  });
}, { root: stage });
items.forEach(it => io.observe(it));
29 · Spring Slider
Inter
// 버튼 → spring physics 로 thumb 이동
btns.forEach(b => b.addEventListener('click', () => {
  const p = +b.dataset.pos;
  lm.motion({
    targets: thumb,
    left: [thumb.style.left || '0%', (p*100)+'%'],
    duration: 800,
    easing: lm.spring({ stiffness: 160, damping: 12 }),
  });
}));
0
30 · Counter Tick
Inter
// 0 → 9999 부드럽게 카운트업
lm.motion({
  targets: { v: 0 },
  v: 9999,
  duration: 2000,
  easing: 'outQuart',
  update: t => el.textContent = Math.round(t*9999).toLocaleString(),
});
FX & Particles 이펙트 · 파티클
31 · Particle Burst
FX
// 클릭 위치에서 파티클이 사방으로 폭발
function burst(x, y) {
  for (let i = 0; i < 30; i++) {
    const p = make('particle');
    p.style.background = randomColor();
    const ang = (i/30)*Math.PI*2;
    const dist = lm.random(40, 100);
    lm.motion({
      targets: p,
      translateX: [0, Math.cos(ang)*dist],
      translateY: [0, Math.sin(ang)*dist],
      opacity: [1, 0], scale: [1, 0.3],
      duration: 800, easing: 'outQuart',
      complete: () => p.remove(),
    });
  }
}
32 · Confetti Drop
FX
// 위에서 색종이가 회전하며 떨어짐
for (let i = 0; i < 40; i++) {
  const c = make('confetti');
  c.style.left = lm.random(0, 100) + '%';
  c.style.background = randomColor();
  lm.motion({
    targets: c,
    translateY: [0, 240],
    rotate: [0, lm.random(-360, 720)],
    duration: lm.random(1500, 3000),
    delay: i*30,
    easing: 'inQuad',
  });
}
MOVE MOUSE
33 · Cursor Trail
FX
// 마우스 이동마다 작은 점이 사라짐
stage.addEventListener('pointermove', e => {
  const r = stage.getBoundingClientRect();
  const dot = make('trail-dot');
  dot.style.left = (e.clientX - r.left) + 'px';
  dot.style.top  = (e.clientY - r.top) + 'px';
  lm.motion({
    targets: dot,
    scale: [1, 0], opacity: [1, 0],
    duration: 600,
    complete: () => dot.remove(),
  });
});
34 · Magnetic Field
FX
// 격자 점이 마우스에서 멀어지는 방향으로 휨
field.addEventListener('pointermove', e => {
  dots.forEach(d => {
    const dx = d.x - mx, dy = d.y - my;
    const dist = Math.hypot(dx, dy);
    const push = Math.max(0, 60 - dist);
    d.el.style.transform = `translate(${dx/dist*push}px,${dy/dist*push}px)`;
  });
});
0
USERS
0
RATE %
0
EVENTS
0
SPRINTS
35 · Stat Counters
Inter
// 여러 통계 동시 카운트업
nums.forEach(n => {
  const target = +n.dataset.target;
  lm.motion({
    targets: { v: 0 },
    v: target,
    duration: 1500,
    easing: 'outQuart',
    update: t => n.textContent = Math.round(t*target).toLocaleString(),
  });
});
36 · Timeline Sequence
FX
// 타임라인으로 순차 합성 (overlap 가능)
const tl = lm.timeline({ easing: 'outBack', duration: 800 });
tl.add({ targets: '#seq36a', scale: [0,1.4,1], rotate: [0,180] })
  .add({ targets: '#seq36b', translateY: [-30,0] }, '-=400')
  .add({ targets: '#seq36c', scale: [0,1], opacity: [0,1] }, '-=400')
  .play();
Loaders & Spinners 로딩 인디케이터
37 · Spinner Ring
Loader
// 컬러 그라디언트 보더 회전
lm.motion({
  targets: el,
  rotate: [0, 360],
  duration: 900,
  loop: true,
  easing: 'linear',
});
38 · Pulse Dots
Loader
// 점 3개가 차례로 커지고 작아지기
lm.motion({
  targets: '#pulse38 > div',
  scale: [0.6, 1.4],
  opacity: [0.4, 1],
  duration: 600,
  delay: lm.stagger(150),
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
});
39 · Progress Bar
Loader
// 0% → 100% 채워지면서 그라디언트 흐르기
lm.motion({
  targets: { p: 0 },
  p: 100,
  duration: 2000,
  easing: 'outQuart',
  update: t => {
    fill.style.width = (t*100) + '%';
    fill.style.backgroundPosition = (t*200) + '% 0';
  },
});
0%
40 · Progress Ring
Loader
// pathLength=100 으로 dashoffset이 곧 % 진행률
lm.motion({
  targets: { p: 0 },
  p: 87,
  duration: 2000,
  easing: 'outQuart',
  update: t => {
    const pct = t*87;
    ring.setAttribute('stroke-dashoffset', 100-pct);
    text.textContent = Math.round(pct) + '%';
  },
});
41 · Skeleton Shimmer
Loader
// gradient background-position 으로 빛 흐르기
lm.motion({
  targets: { p: 0 },
  p: 1,
  duration: 1400,
  loop: true,
  easing: 'linear',
  update: t => lines.forEach(L =>
    L.style.backgroundPosition = (-100 + t*200) + '% 0'),
});
42 · Dot Wave
Loader
// 16개 도트가 사인파처럼 위아래
lm.motion({
  targets: '#dotwave42 > div',
  height: ['8px', '40px'],
  duration: 700,
  delay: lm.stagger(50, { from: 'center' }),
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
});
Game & Reward 게임 피드백
H
T
43 · Coin Flip
Game
// rotateY 회전 + translateY 포물선
const spins = 5 + lm.random(0, 2);
lm.motion({
  targets: coin,
  rotateY: [0, 360*spins],
  duration: 1500,
  easing: 'outQuart',
  update: t => coin.style.translate = `0 ${-Math.sin(t*Math.PI)*60}px`,
});
44 · Card Shuffle
Game
// 5장이 섞이며 펼쳐졌다가 다시 모이기
const tl = lm.timeline({ duration: 600, easing: 'outBack' });
cards.forEach((c, i) => tl.add({
  targets: c,
  translateX: (i-2) * 50,
  rotate:   (i-2) * 8,
}, i*60));
tl.play();
45 · Dice Roll
Game
// rotateX/Y 무작위 spin 후 정해진 면 정지
const rx = 720 + lm.random(0, 3)*90;
const ry = 720 + lm.random(0, 3)*90;
lm.motion({
  targets: dice,
  rotateX: [0, rx],
  rotateY: [0, ry],
  duration: 1400,
  easing: 'outCubic',
});
0
46 · Score Pop
Game
// 점수 카운트 + 작은 +N 라벨이 위로 떠올라 사라짐
lm.motion({
  targets: { v: prevScore },
  v: newScore,
  duration: 800,
  easing: 'outQuart',
  update: t => el.textContent = Math.round(prevScore + (newScore-prevScore)*t),
});
spawnPopLabel('+250');
0
×
COMBO
47 · Combo Counter
Game
// 콤보 증가 시마다 통통 + scale 펄스
function addCombo() {
  combo++;
  el.textContent = combo;
  lm.motion({
    targets: el,
    scale: [1.5, 1],
    rotate: [lm.random(-12,12), 0],
    duration: 400,
    easing: 'outBack',
  });
}
LEVEL UP!
48 · Level Up
Game
// 텍스트 등장 + 빛줄기 12개 회전 폭발
lm.motion({ targets: text, scale: [0, 1.3, 1], opacity: [0, 1], duration: 800, easing: 'outBack' });
rays.forEach((r, i) => lm.motion({
  targets: r,
  rotate: [(i*30), (i*30) + 90],
  scale: [0, 1], opacity: [1, 0],
  duration: 1000, easing: 'outQuart',
}));
Physics 물리 시뮬레이션
49 · Pendulum
Physics
// 감쇠 진동: angle = A·cos(ωt)·e^(-bt)
lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: 5000,
  loop: true,
  easing: 'linear',
  update: t => {
    const a = 45 * Math.cos(t*12) * Math.exp(-t*0.4);
    arm.style.transform = `rotate(${a}deg)`;
  },
});
50 · Spring Chain
Physics
// 8개 노드가 마우스를 따라 지연된 chain 운동
nodes.forEach((n, i) => {
  n.x += (target.x - n.x) * (0.15 + i*0.05);
  n.y += (target.y - n.y) * (0.15 + i*0.05);
  target = n;
});
51 · Bouncing Ball
Physics
// outBounce 이징 + scale로 충돌 압축
lm.motion({
  targets: ball,
  translateY: [0, 150],
  scaleY: [1, 0.7, 1],
  duration: 1500,
  loop: true,
  direction: 'alternate',
  easing: 'outBounce',
});
52 · Falling Leaves
Physics
// 잎이 좌우 흔들리며 회전+낙하
leaves.forEach(L => lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: lm.random(3000,5000),
  loop: true,
  easing: 'linear',
  update: t => {
    L.style.transform = `translate(${Math.sin(t*8)*30}px, ${t*220}px) rotate(${t*720}deg)`;
  },
}));
53 · Liquid Wave
Physics
// 사인파 두 개 합성으로 출렁이는 액체 표면
function buildPath(ph) {
  let d = 'M 0 180';
  for (let x = 0; x <= 220; x += 5) {
    const y = 90 + Math.sin(x/40+ph)*10 + Math.sin(x/15+ph*2)*5;
    d += ` L ${x} ${y}`;
  }
  return d + ' L 220 180 Z';
}
54 · Smoke Trail
Physics
// 연기 puff: 위로 떠오르며 커지고 사라짐
lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: 2000,
  easing: 'outQuart',
  update: t => {
    puff.style.transform = `translate(-50%, ${-t*120}px) scale(${1 + t*2})`;
    puff.style.opacity = 1 - t;
  },
});
Reveal & Transitions 등장·전환
SHOW
55 · Curtain Reveal
Reveal
// 좌우 커튼이 양옆으로 슬라이드
lm.motion({ targets: '.curtain.left',  translateX: [0, '-100%'], duration: 1000, easing: 'ioCubic' });
lm.motion({ targets: '.curtain.right', translateX: [0, '100%'],  duration: 1000, easing: 'ioCubic' });
REVEAL
56 · Iris Wipe
Reveal
// clip-path circle 반지름이 0 → max
lm.motion({
  targets: { r: 0 },
  r: 160,
  duration: 1200,
  easing: 'ioCubic',
  update: t => mask.style.clipPath = `circle(${(1-t)*160}px at 50% 50%)`,
});
57 · Slide Carousel
Reveal
// translateX -100% 단위로 자동 회전
let idx = 0;
setInterval(() => {
  idx = (idx + 1) % 3;
  lm.motion({
    targets: track,
    translateX: `${-idx*100}%`,
    duration: 600,
    easing: 'ioCubic',
  });
}, 2000);
58 · Flip Reveal
Reveal
// 카드들이 도미노처럼 차례로 뒤집힘
lm.motion({
  targets: cards,
  rotateX: [90, 0],
  opacity: [0, 1],
  duration: 700,
  delay: lm.stagger(80),
  easing: 'outBack',
});
STRIPES
59 · Stripe Mask
Reveal
// 7줄 줄무늬가 좌/우 번갈아 슬라이드
stripes.forEach((s, i) => lm.motion({
  targets: s,
  translateX: [0, i % 2 ? '100%' : '-100%'],
  duration: 800,
  delay: i*80,
  easing: 'ioCubic',
}));
GO
60 · Zoom Burst
Reveal
// 스케일 0 → 6 + opacity 1 → 0 으로 화면 밖으로 폭발
lm.motion({
  targets: el,
  scale: [0, 6],
  opacity: [1, 0],
  duration: 1200,
  easing: 'outQuart',
});
Glitch & Distortion 왜곡 이펙트
GLITCH
61 · Glitch Text
Glitch
// ::before / ::after 두 레이어 상하 흔들림
lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: 200,
  loop: true,
  easing: 'linear',
  update: () => {
    el.style.setProperty('--gx1', lm.random(-3,3) + 'px');
    el.style.setProperty('--gx2', lm.random(-3,3) + 'px');
  },
});
RGBRGBRGB
62 · RGB Split
Glitch
// 3개 채널이 미세하게 다른 위치로 이동
lm.motion({
  targets: layers,
  translateX: [0, (el, i) => (i-1)*6],
  duration: 300,
  loop: true,
  direction: 'alternate',
});
CRT MONITOR
63 · CRT Scanline
Glitch
// 가로 빛줄기가 위에서 아래로 무한 통과
lm.motion({
  targets: scan,
  translateY: ['-10px', '140px'],
  duration: 2200,
  loop: true,
  easing: 'linear',
});
DISTORT
64 · SVG Distort
Glitch
// feTurbulence baseFrequency 변화로 왜곡 강도 조절
lm.motion({
  targets: { f: 0.02 },
  f: 0.08,
  duration: 2000,
  loop: true,
  direction: 'alternate',
  update: t => turb.setAttribute('baseFrequency', 0.02 + t*0.06),
});
65 · Pixelate Reveal
Glitch
// CSS image-rendering + filter blur로 픽셀화 풀어짐
lm.motion({
  targets: { p: 20 },
  p: 0,
  duration: 1500,
  easing: 'outQuart',
  update: t => el.style.filter = `blur(${(1-t)*16}px) contrast(${1+t*0.3})`,
});
66 · Static Noise
Glitch
// Canvas ImageData에 무작위 픽셀 매 프레임 그리기
function drawNoise() {
  const img = ctx.createImageData(w, h);
  for (let i = 0; i < img.data.length; i += 4) {
    const v = Math.random() * 255;
    img.data[i] = img.data[i+1] = img.data[i+2] = v;
    img.data[i+3] = 255;
  }
  ctx.putImageData(img, 0, 0);
}
Data Visualization 차트·그래프
67 · Bar Chart Grow
Data
// 막대들이 0에서 데이터 높이로 차례로 자람
lm.motion({
  targets: bars,
  height: (el, i) => data[i] + 'px',
  duration: 900,
  delay: lm.stagger(60),
  easing: 'outBack',
});
68 · Pie Chart
Data
// pathLength=100 이라 dasharray가 곧 % 비율
const percentages = [35, 25, 40];
let offset = 0;
slices.forEach((s, i) => {
  s.setAttribute('stroke-dashoffset', -offset);
  lm.motion({
    targets: s,
    strokeDasharray: ['0 100', `${percentages[i]} 100`],
    duration: 1000,
    delay: i*200,
  });
  offset += percentages[i];
});
69 · Line Chart Draw
Data
// 데이터 포인트가 차례로 그려지며 polyline 연장
lm.motion({
  targets: { i: 0 },
  i: data.length,
  duration: 1500,
  easing: 'outCubic',
  update: t => {
    const n = Math.floor(t * data.length);
    line.setAttribute('points',
      data.slice(0, n+1).map((d,i) => `${i*30},${140-d}`).join(' '));
  },
});
70 · Heatmap
Data
// 셀 색상이 stagger로 어두움 → 강도 색
cells.forEach((c, i) => {
  const v = data[i];
  lm.motion({
    targets: c,
    backgroundColor: ['rgba(6,182,212,0.05)', `rgba(6,182,212,${v})`],
    duration: 400,
    delay: i*15,
  });
});
0%
71 · Donut Chart
Data
// progress ring과 동일하지만 두께가 도넛 비율
lm.motion({
  targets: { p: 0 },
  p: 73,
  duration: 1800,
  easing: 'outQuart',
  update: t => {
    ring.setAttribute('stroke-dashoffset', 100 - t*73);
    text.textContent = Math.round(t*73) + '%';
  },
});
72 · Spark Line
Data
// 미니 라인차트 + 채워진 영역
lm.motion({
  targets: { i: 0 },
  i: data.length,
  duration: 1500,
  update: t => redrawSpark(Math.floor(t*data.length)),
});
Cute & Mascot 귀여운 모션
🐋
73 · Whale Bob
Cute
// 위아래 출렁 + 좌우 미세 흔들 + 호흡 scale
lm.motion({
  targets: el,
  translateY: [0, -12],
  rotate: [-3, 3],
  scale: [1, 1.05],
  duration: 2000,
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
});
74 · Bubble Float
Cute
// 거품이 아래에서 위로 떠오르며 좌우 흔들
lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: 3000,
  easing: 'linear',
  update: t => {
    bubble.style.transform =
      `translate(${Math.sin(t*6)*15}px, ${-t*200}px) scale(${1+t*0.3})`;
    bubble.style.opacity = 1-t;
  },
});
75 · Sparkle Trail
Cute
// 마우스 따라 ✦ 별이 회전하며 사라짐
stage.addEventListener('pointermove', e => {
  const sp = make('sparkle');
  sp.style.left = e.x + 'px';
  sp.style.top  = e.y + 'px';
  lm.motion({
    targets: sp,
    rotate: [0, 360],
    scale: [1.5, 0],
    opacity: [1, 0],
    duration: 800,
    complete: () => sp.remove(),
  });
});
76 · Star Twinkle
Cute
// 별들이 반짝반짝 — 무작위 opacity
stars.forEach(s => lm.motion({
  targets: s,
  opacity: [0.2, 1],
  scale: [0.6, 1.2],
  duration: lm.random(800,1800),
  delay: lm.random(0,2000),
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
}));
77 · Heart Float
Cute
// 클릭하면 ❤️ 가 위로 떠오름 (좋아요 효과)
stage.addEventListener('click', e => {
  const h = make('heart');
  h.textContent = '❤️';
  h.style.left = e.x + 'px';
  h.style.top  = e.y + 'px';
  lm.motion({
    targets: h,
    translateY: [0, -100],
    translateX: [0, lm.random(-30,30)],
    scale: [0.5, 1.5],
    opacity: [1, 0],
    duration: 1200,
  });
});
78 · Cloud Drift
Cute
// 구름들이 다른 속도로 좌→우 영구 흐름
clouds.forEach(c => lm.motion({
  targets: c,
  translateX: ['-60px', '400px'],
  duration: lm.random(8000,15000),
  loop: true,
  easing: 'linear',
}));
UI Notifications 알림·모달
79 · Toast Slide
UI
// 오른쪽에서 들어오고 3초 후 사라짐
lm.motion({
  targets: toast,
  translateX: ['120%', '0%'],
  opacity: [0, 1],
  duration: 500,
  easing: 'outBack',
});
setTimeout(() => lm.motion({
  targets: toast,
  translateX: ['0%', '120%'],
  duration: 400,
  complete: () => toast.remove(),
}), 2500);
80 · Modal Pop
UI
// 백드롭 페이드 + 박스 scale spring
lm.motion({ targets: backdrop, opacity: [0, 1], duration: 300 });
lm.motion({
  targets: box,
  scale: [0.7, 1],
  opacity: [0, 1],
  duration: 700,
  easing: lm.spring({ stiffness: 160, damping: 12 }),
});
HOVER ME
81 · Tooltip Flip
UI
// rotateX로 위에서 펼쳐지듯 등장
lm.motion({
  targets: bubble,
  translateY: ['10px', '0px'],
  rotateX: [-90, 0],
  opacity: [0, 1],
  duration: 350,
  easing: 'outBack',
});
notifications
3
82 · Badge Pulse
UI
// 빨간 점이 펄스 + 숫자 등장 시 통통
lm.motion({
  targets: dot,
  scale: [1, 1.3, 1],
  duration: 600,
  loop: true,
  easing: 'ioSine',
});
83 · Snackbar
UI
// 아래에서 슬라이드 업, UNDO 버튼 포함
lm.motion({
  targets: snack,
  translateY: ['80px', '0px'],
  opacity: [0, 1],
  duration: 450,
  easing: 'outBack',
});
84 · Banner Drop
UI
// 위에서 바운스로 떨어짐 (공지 배너)
lm.motion({
  targets: banner,
  translateY: ['-100%', '0%'],
  duration: 700,
  easing: 'outBounce',
});
Audio Visual 음악 시각화
85 · EQ Bars
Audio
// 12개 막대가 각자 다른 주파수로 출렁
bars.forEach((b, i) => lm.motion({
  targets: b,
  height: ['8px', lm.random(40,120) + 'px'],
  duration: lm.random(300,600),
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
}));
86 · Waveform
Audio
// 사인파 + 무작위 변조로 음성 파형 흉내
lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: 2000,
  loop: true,
  easing: 'linear',
  update: t => bars.forEach((b, i) => {
    const h = Math.abs(Math.sin(i*.4 + t*10)) * 40 + 4;
    b.style.height = h + 'px';
  }),
});
VU METER
87 · VU Meter
Audio
// 무작위 음량으로 게이지 출렁
setInterval(() => {
  lm.motion({
    targets: fill,
    width: [fill.style.width, lm.random(30,95) + '%'],
    duration: 200,
    easing: 'outQuart',
  });
}, 300);
88 · Spectrum
Audio
// 주파수 스펙트럼 곡선이 출렁
lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: 3000,
  loop: true,
  easing: 'linear',
  update: t => {
    let d = 'M 0 100';
    for (let x = 0; x <= 240; x += 8) {
      const y = 100 - (Math.sin(x/20+t*10) + Math.cos(x/7+t*8))*25 - 30;
      d += ` L ${x} ${y}`;
    }
    path.setAttribute('d', d);
  },
});
BPM
89 · Beat Pulse
Audio
// BPM 박자에 맞춰 통통 (140 BPM = 428ms 주기)
lm.motion({
  targets: el,
  scale: [1, 1.25],
  duration: 214,
  loop: true,
  direction: 'alternate',
  easing: 'outQuad',
});
90 · DJ Spin
Audio
// 턴테이블 LP 회전 (33 RPM = 1800ms/회전)
lm.motion({
  targets: disc,
  rotate: [0, 360],
  duration: 1800,
  loop: true,
  easing: 'linear',
});
Paper & Folding 종이 효과
A
B
91 · Page Turn
Paper
// 책 페이지가 왼쪽 모서리 기준으로 넘어감
lm.motion({
  targets: book,
  rotateY: [0, -180],
  duration: 1500,
  loop: true,
  direction: 'alternate',
  easing: 'ioCubic',
});
F
B
R
L
T
D
92 · Cube Unfold
Paper
// 큐브 6면이 펼쳐져 십자가 모양 → 다시 모임
lm.motion({
  targets: cube,
  rotateX: [-20, 10],
  rotateY: [20, 45],
  duration: 3000,
  loop: true,
  direction: 'alternate',
});
93 · Origami Fold
Paper
// 삼각형 점좌표 보간으로 종이접기 흉내
const A = "100,30 150,100 50,100";
const B = "100,100 150,30 50,30";
lm.motion({
  targets: { t: 0 },
  t: 1,
  duration: 2000,
  loop: true,
  direction: 'alternate',
  update: t => poly.setAttribute('points', interp(A, B, t)),
});
94 · Domino Fall
Paper
// 12개 도미노가 차례로 90도 쓰러짐
lm.motion({
  targets: dominos,
  rotate: [0, 90],
  duration: 300,
  delay: lm.stagger(120),
  easing: 'inQuad',
});
▸ Section 1
접근 가능한 콘텐츠
▸ Section 2
두 번째 섹션
▸ Section 3
세 번째 섹션
95 · Accordion
Paper
// height auto 보간 (실제 높이 측정 후 px로)
heads.forEach(h => h.addEventListener('click', () => {
  const body = h.nextElementSibling;
  const isOpen = body.classList.contains('open');
  lm.motion({
    targets: body,
    height: isOpen ? [body.scrollHeight, 0] : [0, body.scrollHeight],
    duration: 350, easing: 'ioCubic',
  });
  body.classList.toggle('open');
}));
96 · Vertical Wave
Paper
// 18개 세로 막대가 사인파처럼 키 변화
lm.motion({
  targets: bars,
  height: ['30%', '95%'],
  duration: 800,
  delay: lm.stagger(40, { from: 'center' }),
  loop: true,
  direction: 'alternate',
  easing: 'ioSine',
});
Educational & Math 학습용 시각화
-2 -1 0 1 2 -2
97 · Number Line
Math
// 점이 -2 → +2 사이를 spring 으로 이동, 라벨 동기화
lm.motion({
  targets: { v: -2 },
  v: 2,
  duration: 2500,
  loop: true,
  direction: 'alternate',
  easing: 'ioCubic',
  update: t => {
    const v = -2 + t*4;
    const x = 120 + v*40;
    dot.setAttribute('cx', x);
    label.setAttribute('x', x);
    label.textContent = v.toFixed(1);
  },
});
x y O
98 · Coordinate Plot
Math
// 좌표 (x,y) 점이 차례로 plot — 좌표→픽셀 변환
const pts = [[-2,3],[1,2],[2,-1],[-1,-2]];
pts.forEach((p, i) => {
  const [x, y] = [100 + p[0]*20, 100 - p[1]*20];
  const c = circle(x, y, 0);
  lm.motion({
    targets: c,
    r: [0, 5],
    duration: 400,
    delay: i*200,
    easing: 'outBack',
  });
});
x y y = sin(x)
99 · Function Graph
Math
// y=sin(x) 곡선이 좌→우로 그려짐 + 진행점
lm.motion({
  targets: { p: 0 },
  p: 1,
  duration: 2500,
  loop: true,
  easing: 'linear',
  update: t => {
    const end = t * 220;
    let d = 'M 0 ' + (90 - Math.sin(0)*40);
    for (let x = 0; x <= end; x += 2) {
      const mathX = (x - 110) / 25;
      const y = 90 - Math.sin(mathX) * 40;
      d += ` L ${x} ${y}`;
    }
    path.setAttribute('d', d);
  },
});
0/8
100 · Fraction Pie
Math
// 8조각 파이가 0/8 → 5/8 까지 차례로 채워짐
function slicePath(i) {
  const a0 = (i/8)*Math.PI*2 - Math.PI/2;
  const a1 = ((i+1)/8)*Math.PI*2 - Math.PI/2;
  const p0 = [80+Math.cos(a0)*60, 80+Math.sin(a0)*60];
  const p1 = [80+Math.cos(a1)*60, 80+Math.sin(a1)*60];
  return `M 80 80 L ${p0} A 60 60 0 0 1 ${p1} Z`;
}
101 · Geometry Compass
Math
// 컴퍼스가 펼쳐졌다가 회전하며 원 그리기
const tl = lm.timeline();
// 1. 다리 펼치기
tl.add({ targets: armA, attr: { rotate: '-30 100 20' }, duration: 600 })
  .add({ targets: armB, attr: { rotate: '30 100 20' }, duration: 600 }, '-=600')
// 2. 펜이 원호 그리기
  .add({
    targets: { ang: 0 },
    ang: 2*Math.PI,
    duration: 2000,
    update: t => drawArc(t),
  });
2x + 5 = 13
2x + 5 - 5 = 13 - 5- 5
2x = 8
2x ÷ 2 = 8 ÷ 2÷ 2
x = 4
102 · Equation Solve
Math
// 방정식 풀이 단계가 차례로 등장 + 활성 줄 강조
lines.forEach((L, i) => lm.motion({
  targets: L,
  translateX: [-12, 0],
  opacity: [0, 1],
  duration: 500,
  delay: i*600,
  easing: 'outBack',
  begin: () => L.classList.add('active'),
  complete: () => {
    L.classList.remove('active');
    L.classList.add('done');
  },
}));
a²+b²=c²
103 · Pythagorean
Math
// 직각삼각형 → 세 변에 정사각형 → 식 등장
const tl = lm.timeline({ duration: 600, easing: 'outBack' });
tl.add({ targets: sqA, opacity: [0,1] })
  .add({ targets: sqB, opacity: [0,1] }, '-=300')
  .add({ targets: sqC, opacity: [0,1] }, '-=300')
  .add({ targets: eq, opacity: [0,1], scale: [0.7,1] });
tl.play();
A B A+B
104 · Vector Sum
Math
// 벡터 A 그리기 → 끝에서 B 시작 → 합 결과 강조
tl.add({ targets: { p: 0 }, p: 1, update: t => vecA.setAttribute('x2', 50+t*80) })
  .add({ targets: { p: 0 }, p: 1, update: t => {
    vecB.setAttribute('x2', 130+t*40);
    vecB.setAttribute('y2', 110-t*60);
  } })
  .add({ targets: { p: 0 }, p: 1, update: t => {
    vecC.setAttribute('x2', 50+t*120);
    vecC.setAttribute('y2', 140-t*90);
  } });
A B C
105 · Triangle SSS
Math
// SSS 작도: 밑변 → 두 호 → 교점 → 변 연결
// C = (Ax + (Bx-Ax)·k - h·(By-Ay)/d, Ay + ...) 정확한 교점 계산
const d = Math.hypot(Bx-Ax, By-Ay);
const k = (b*b - a*a + d*d) / (2*d);
const h = Math.sqrt(b*b - k*k);
const Cx = Ax + k*(Bx-Ax)/d - h*(By-Ay)/d;
const Cy = Ay + k*(By-Ay)/d + h*(Bx-Ax)/d;
시작
106 · Probability Tree
Math
// 동전 2회 던지기 트리 — 가지 + 노드 + 확률 라벨
const outcomes = [
  { path: ['H'], prob: 0.5 },
  { path: ['T'], prob: 0.5 },
  { path: ['H','H'], prob: 0.25 },
  ...
];
outcomes.forEach((o, i) => drawBranch(o, i*200));
107 · Normal Curve
Math
// 막대그래프 → 정규분포 곡선 fitting
function gauss(x, mu, sig) {
  return Math.exp(-0.5*((x-mu)/sig)**2) / (sig*Math.sqrt(2*Math.PI));
}
// y축 스케일 후 path build
θ = 0°
108 · Matrix Rotate
Math
// 회전 행렬 R(θ) 적용 — 사각형이 원점 기준 회전
function rotate(p, θ) {
  const c = Math.cos(θ), s = Math.sin(θ);
  return [p[0]*c - p[1]*s, p[0]*s + p[1]*c];
}
Science 과학 시각화
109 · Atom Orbit
Science
// 3개 전자가 다른 각도의 타원 궤도를 돌기
lm.motion({
  targets: { θ: 0 },
  θ: 2*Math.PI,
  duration: 3000, loop: true,
  update: t => {
    // 타원 매개변수 (a*cosθ, b*sinθ) 후 회전
    e1.setAttribute('cx', ...);
  },
});
110 · Wave Interference
Science
// 두 사인파 합성 = 보강/상쇄 간섭
const y1 = Math.sin(x/15 + ph) * 25;
const y2 = Math.sin(x/15 - ph) * 25;
const sum = y1 + y2; // → 2·sin(x/15)·cos(ph) 형태
KE PE
111 · Pendulum Energy
Science
// 진자: KE = ½mv², PE = mgh — 합 일정 (에너지 보존)
const θ = θ_max * Math.cos(ω*t);
const h = L*(1 - Math.cos(θ));
const v = L*ω*Math.sin(θ_max*Math.sin(ω*t)); // 근사
// 막대 높이 = 에너지 비율 × 120px
m F=kx
112 · Spring Hooke
Science
// 스프링이 늘어났다 줄어듦 (단순조화운동)
function springPath(L) {
  let d = 'M 20 100 L 30 100';
  const coils = 8, w = (L - 20) / coils;
  for (let i = 0; i < coils; i++) {
    d += ` L ${30 + i*w + w/4} 88 L ${30 + i*w + 3*w/4} 112`;
  }
  return d + ` L ${L} 100`;
}
1/v − 1/u = 1/f
113 · Lens Refraction
Science
// 볼록렌즈 3대 광선 작도 → 상 위치 결정
// 1) 광축 평행 광선 → 초점 통과
// 2) 렌즈 중심 통과 → 직진
// 3) 초점 통과 광선 → 광축 평행
// 교점 = 상의 위치 (1/v − 1/u = 1/f 검증)
114 · DNA Helix
Science
// 두 사인파 가닥 + 사이 염기쌍 — 회전하는 이중나선
for (let y = 0; y <= 180; y += 8) {
  const x1 = 110 + Math.sin(y/20 + ph) * 35;
  const x2 = 110 - Math.sin(y/20 + ph) * 35;
  // 가닥 점 + 염기쌍 라인
}
English & Language 영어 학습
115 · Alphabet Pop
English
// 26개 알파벳이 stagger로 통통 등장
lm.motion({
  targets: letters,
  scale: [0, 1.2, 1],
  rotate: [-30, 0],
  opacity: [0, 1],
  duration: 600,
  delay: lm.stagger(50),
  easing: 'outBack',
});
S
T
U
D
Y
116 · Word Decompose
English
// "STUDY" 글자가 위로 솟아 분리되었다 다시 합침
lm.motion({
  targets: letters,
  translateY: [0, -40],
  rotate: [0, (el, i) => (i-2)*10],
  duration: 700,
  delay: lm.stagger(100),
  loop: true,
  direction: 'alternate',
  easing: 'outBack',
});
ed/ɛ/u/ʊ/ca/ˈkeɪ/tion/ʃən/
117 · Syllable Stress
English
// "education" 음절별 등장 + 강세 음절 강조
lm.motion({
  targets: syllables,
  scale: [0, 1],
  opacity: [0, 1],
  translateY: [10, 0],
  duration: 500,
  delay: lm.stagger(200),
  easing: 'outBack',
});
// 강세 음절은 한번 더 통통
lm.motion({
  targets: stressedSyl,
  scale: [1, 1.2, 1],
  delay: 800, duration: 500,
});
apple
사과
118 · Dictation Card
English
// 단어 카드 뒤집어서 한국어 의미 보여주기
lm.motion({
  targets: card,
  rotateY: [0, 180],
  duration: 1000,
  loop: true,
  direction: 'alternate',
  easing: 'ioCubic',
});
S NP VP The fox jumps
119 · Sentence Tree
English
// "The fox jumps" 구문 트리 — S → NP + VP 분기
const tl = lm.timeline();
tl.add({ targets: '#S', opacity: [0,1], scale: [0,1] })
  .add({ targets: branches, x2: ... })
  .add({ targets: [NP, VP], opacity: [0,1] })
  .add({ targets: leaves, opacity: [0,1] });
tl.play();
NOW
P
N
F
I ran
I run
I will run
120 · Tense Timeline
English
// 과거 → 현재 → 미래 시제 마커가 차례로 나타남
lm.motion({
  targets: markers,
  scale: [0, 1],
  opacity: [0, 1],
  duration: 500,
  delay: lm.stagger(400),
  easing: 'outBack',
});
Korean & 한글 한국어 학습
+
+
121 · 자모음 결합
Korean
// ㄱ + ㅏ + ㅁ → "감" 결합
const tl = lm.timeline();
tl.add({ targets: pieces, scale: [0,1], delay: lm.stagger(300) })
  .add({ targets: arrows, opacity: [0,1] }, '-=300')
  .add({ targets: result, scale: [0,1.3,1], opacity: [0,1] });
tl.play();
[학]+[교]=[학꾜]
122 · 받침 음운 변동
Korean
// 학교 → [학꾜] 된소리되기 — 받침 ㄱ이 다음 ㄱ을 ㄲ으로
lm.motion({
  targets: [a, b],
  scale: [0.7, 1],
  opacity: [0, 1],
  duration: 600,
  delay: lm.stagger(200),
  easing: 'outBack',
});
// 결과 [학꾜] 강조 — 통통 펄스
lm.motion({
  targets: result, scale: [1, 1.3, 1],
  delay: 800, duration: 600,
});
山 (산)
123 · 한자 획순
Korean
// 山 한자 4획이 stroke-draw로 차례로 그려짐
strokes.forEach((s, i) => {
  const len = lm.pathLength(s);
  lm.motion({
    targets: s,
    strokeDashoffset: [len, 0],
    duration: 500,
    delay: i*500,
    easing: 'ioCubic',
  });
});
이 몸이 죽고 죽어 일백 번 고쳐 죽어
백골이 진토되어 넋이라도 있고 없고
향한 일편 단심이야 가실 줄이 있으랴
124 · 시조 낭독
Korean
// 정몽주 단심가 — 3장이 차례로 등장 + 핵심어 강조
lm.motion({
  targets: lines,
  translateX: [-20, 0],
  opacity: [0, 1],
  duration: 800,
  delay: lm.stagger(1500),
  easing: 'outQuart',
});
나는학교에간다
125 · 띄어쓰기 마커
Korean
// "나는학교에간다" → 띄어쓰기 마커가 차례로 들어감
markers.forEach((m, i) => lm.motion({
  targets: m,
  width: ['0px', '8px'],
  duration: 400,
  delay: 800 + i*600,
  easing: 'outBack',
}));
126 · 가나다 매트릭스
Korean
// 14자음 × 7모음 = 98칸 매트릭스가 stagger로 등장
const consonants = 'ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎ';
const vowels = 'ㅏㅑㅓㅕㅗㅛㅜ';
lm.motion({
  targets: cells,
  scale: [0, 1],
  opacity: [0, 1],
  duration: 400,
  delay: lm.stagger(15, { grid: [7, 14], from: 'center' }),
  easing: 'outBack',
});