agentleFS
soilmasssoilmass/motion-design-agent/skills/exaggeration/SKILL.md

exaggeration

Skill7 starsChanged 10 months agoMITView on GitHub

Disney's principle of amplifying motion for clarity and impact. Use when determining animation intensity based on brand attitude, narrative phase, and user context. Maps multipliers to scale, distance, and timing.

The file

Files
SKILL.md

Exaggeration

Pushing motion beyond reality for emotional impact and visual clarity. Real-world motion is often too subtle to read on screen.

Core Principle

Exaggerate motion to communicate clearly. Perfect reality looks lifeless on screen; amplification creates life.

Real life:     Small gesture -> Barely visible on screen
Animation:     Exaggerated gesture -> Clear, readable, emotional

Token Reference

Load from: tokens/exaggeration.tokens.json

TokenMultiplierScale BoostUse Case
subtle1.1x1.05xEnterprise, professional
moderate1.3x1.15xConsumer apps
expressive1.6x1.30xPlayful, gaming
dramatic2.0x1.50xHero moments, celebrations
cartoon2.5x2.00xKids, character animation

Applying Exaggeration

Scale

// Base scale change: 10%
const baseScale = 1.1;

// With exaggeration multiplier
const exaggeratedScale = 1 + (baseScale - 1) * multiplier;

// subtle (1.1x):   1 + 0.1 * 1.1 = 1.11
// moderate (1.3x): 1 + 0.1 * 1.3 = 1.13
// dramatic (2.0x): 1 + 0.1 * 2.0 = 1.20

Distance

// Base movement: 20px
const baseDistance = 20;

// With exaggeration multiplier
const exaggeratedDistance = baseDistance * multiplier;

// subtle (1.1x):   22px
// moderate (1.3x): 26px
// dramatic (2.0x): 40px

Timing (optional stretch)

// Base duration: 300ms
const baseDuration = 300;

// With timing stretch (usually subtle)
const stretchedDuration = baseDuration * timingStretch;

// subtle (1.0x):   300ms (no change)
// moderate (1.1x): 330ms
// dramatic (1.3x): 390ms

Context Mapping

By Use Case

ContextLevelRationale
Micro-interactionssubtleFrequent, shouldn't distract
State changesmoderateClear feedback without drama
NotificationsexpressiveNeeds attention
CelebrationsdramaticPeak emotional moment
ErrorsmoderateNoticeable, not alarming
OnboardingexpressiveMemorable first impression

By Brand Attitude

Brand StyleLevelExamples
Elegant/LuxurysubtleHermes, Rolex, Apple
Professionalsubtle-moderateSalesforce, Notion
FriendlymoderateMailchimp, Slack
PlayfulexpressiveDiscord, Duolingo
BolddramaticNike, Spotify

By Narrative Phase

PhaseLevelPurpose
Introductionmoderate-expressiveGrab attention
Rising actionmoderateBuild steadily
ClimaxdramaticMaximum impact
Resolutionsubtle-moderateCalm return

Implementation Presets

CSS Custom Properties

:root {
  /* Exaggeration levels */
  --exaggeration-subtle: 1.1;
  --exaggeration-moderate: 1.3;
  --exaggeration-expressive: 1.6;
  --exaggeration-dramatic: 2.0;

  /* Current level (set via JS or class) */
  --exaggeration: var(--exaggeration-moderate);

  /* Derived values */
  --scale-boost: calc(1 + (0.1 * var(--exaggeration)));
  --distance-boost: calc(20px * var(--exaggeration));
}

/* Usage */
.card-hover {
  transform: scale(var(--scale-boost)) translateY(calc(var(--distance-boost) * -1));
}

/* Brand variants */
.brand-elegant { --exaggeration: var(--exaggeration-subtle); }
.brand-playful { --exaggeration: var(--exaggeration-expressive); }

GSAP

import { gsap } from 'gsap';
import tokens from '../tokens/exaggeration.tokens.json';

// Exaggeration utility
function withExaggeration(baseValue, level = 'moderate', type = 'distance') {
  const config = tokens.exaggeration[level].$value;

  switch (type) {
    case 'distance':
      return baseValue * config.distanceBoost;
    case 'scale':
      return 1 + (baseValue - 1) * config.scaleBoost;
    case 'duration':
      return baseValue * config.timingStretch;
    default:
      return baseValue * config.multiplier;
  }
}

// Example: Button press with configurable exaggeration
function buttonPress(button, level = 'moderate') {
  const baseScale = 0.95;
  const baseY = 2;

  return gsap.to(button, {
    scale: withExaggeration(baseScale, level, 'scale'),
    y: withExaggeration(baseY, level, 'distance'),
    duration: 0.1,
    ease: 'power2.out'
  });
}

// Success celebration with dramatic exaggeration
function celebrate(element) {
  const level = 'dramatic';

  return gsap.timeline()
    .to(element, {
      scale: withExaggeration(1.2, level, 'scale'),
      duration: 0.2,
      ease: 'back.out(2)'
    })
    .to(element, {
      y: withExaggeration(-30, level, 'distance'),
      duration: 0.3,
      ease: 'power2.out'
    })
    .to(element, {
      scale: 1,
      y: 0,
      duration: 0.4,
      ease: 'elastic.out(1, 0.5)'
    });
}

// Contextual exaggeration based on component
function animateComponent(component, action) {
  const contextMap = {
    'button': 'subtle',
    'card': 'moderate',
    'notification': 'expressive',
    'hero': 'dramatic'
  };

  const level = contextMap[component.type] || 'moderate';
  // Apply animation with appropriate level
}

Framer Motion

import { motion } from 'framer-motion';
import tokens from '../tokens/exaggeration.tokens.json';

// Hook for exaggeration values
const useExaggeration = (level: keyof typeof tokens.exaggeration) => {
  const config = tokens.exaggeration[level].$value;

  return {
    scale: (base: number) => 1 + (base - 1) * config.scaleBoost,
    distance: (base: number) => base * config.distanceBoost,
    duration: (base: number) => base * config.timingStretch,
    multiplier: config.multiplier
  };
};

// Component with configurable exaggeration
interface AnimatedCardProps {
  exaggeration?: 'subtle' | 'moderate' | 'expressive' | 'dramatic';
  children: React.ReactNode;
}

export const AnimatedCard: React.FC<AnimatedCardProps> = ({
  exaggeration = 'moderate',
  children
}) => {
  const ex = useExaggeration(exaggeration);

  return (
    <motion.div
      whileHover={{
        y: -ex.distance(10),
        scale: ex.scale(1.02),
        transition: { duration: ex.duration(0.2) }
      }}
      whileTap={{
        scale: ex.scale(0.98)
      }}
    >
      {children}
    </motion.div>
  );
};

// Celebration with maximum exaggeration
const celebrationVariants = {
  initial: { scale: 0, rotate: -180 },
  celebrate: (exLevel: string) => {
    const ex = tokens.exaggeration[exLevel].$value;
    return {
      scale: [0, ex.scaleBoost, 1],
      rotate: [-180, 10 * ex.multiplier, 0],
      transition: {
        duration: 0.6 * ex.timingStretch,
        times: [0, 0.6, 1],
        ease: 'backOut'
      }
    };
  }
};

export const Celebration: React.FC = () => (
  <motion.div
    variants={celebrationVariants}
    initial="initial"
    animate="celebrate"
    custom="dramatic"
  >
    Celebration!
  </motion.div>
);

Remotion

import { useCurrentFrame, interpolate, spring } from 'remotion';
import tokens from '../tokens/exaggeration.tokens.json';

// Exaggeration utilities for video
export const useExaggeration = (level: 'subtle' | 'moderate' | 'expressive' | 'dramatic') => {
  const config = tokens.exaggeration[level].$value;

  return {
    applyToScale: (base: number) => 1 + (base - 1) * config.scaleBoost,
    applyToDistance: (base: number) => base * config.distanceBoost,
    applyToDuration: (baseFrames: number) => Math.round(baseFrames * config.timingStretch)
  };
};

// Example: Title reveal with exaggeration
export const TitleReveal: React.FC<{
  text: string;
  startFrame: number;
  exaggeration?: 'subtle' | 'moderate' | 'expressive' | 'dramatic';
}> = ({ text, startFrame, exaggeration = 'moderate' }) => {
  const frame = useCurrentFrame();
  const ex = useExaggeration(exaggeration);

  const baseScale = 1.5;
  const baseY = 50;
  const baseDuration = 20;

  const progress = spring({
    frame: frame - startFrame,
    fps: 30,
    config: { damping: 12, stiffness: 100 }
  });

  const scale = interpolate(
    progress,
    [0, 0.5, 1],
    [ex.applyToScale(baseScale), ex.applyToScale(1.1), 1]
  );

  const y = interpolate(
    progress,
    [0, 1],
    [ex.applyToDistance(baseY), 0]
  );

  const opacity = interpolate(
    frame,
    [startFrame, startFrame + 10],
    [0, 1],
    { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
  );

  return (
    <div
      style={{
        transform: `scale(${scale}) translateY(${y}px)`,
        opacity
      }}
    >
      {text}
    </div>
  );
};

// Hero moment with dramatic exaggeration
export const HeroMoment: React.FC<{
  triggerFrame: number;
  children: React.ReactNode;
}> = ({ triggerFrame, children }) => {
  const frame = useCurrentFrame();
  const ex = useExaggeration('dramatic');

  // Dramatic entrance
  const progress = interpolate(
    frame,
    [triggerFrame, triggerFrame + ex.applyToDuration(30)],
    [0, 1],
    { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
  );

  const scale = interpolate(
    progress,
    [0, 0.6, 0.8, 1],
    [0, ex.applyToScale(1.3), ex.applyToScale(0.95), 1]
  );

  const y = interpolate(
    progress,
    [0, 0.6, 1],
    [ex.applyToDistance(100), ex.applyToDistance(-20), 0]
  );

  return (
    <div style={{ transform: `scale(${scale}) translateY(${y}px)` }}>
      {children}
    </div>
  );
};

Realistic vs Stylized Spectrum

StyleExaggerationPhysicsUse Case
Photorealistic1.0xAccurateSimulation, data viz
Natural1.1xEnhancedProfessional UI
Stylized1.5xAdjustedConsumer apps
Cartoon2.5x+ImpossibleEntertainment, kids

Accessibility

const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

// Reduce exaggeration for accessibility
const exaggerationLevel = prefersReducedMotion ? 'subtle' : determinedLevel;

// Or cap maximum exaggeration
const cappedLevel = prefersReducedMotion
  ? Math.min(level, tokens.exaggeration.subtle.$value.multiplier)
  : level;

Common Mistakes

  1. Uniform exaggeration - Different contexts need different levels
  2. Always dramatic - Save drama for meaningful moments
  3. Ignoring brand - Exaggeration must match brand personality
  4. Inconsistent - Same actions should have same exaggeration
  5. Overshadowing content - Animation serves content, not vice versa

Related Skills

  • anticipation - Exaggeration level affects anticipation wind-up intensity
  • squash-stretch - Exaggeration determines deformation magnitude
  • follow-through - Exaggeration affects overshoot and settle amplitude
  • secondary-action - Primary/secondary intensity gap follows exaggeration
  • arc-motion - Exaggeration affects arc height and trajectory drama

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.