Rotating Gears
Two SVG gears that truly mesh: 12 and 8 teeth, each turning on its own axis, in opposite directions, at the right speed so the teeth never cross.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →
3 usage examples



How it works
The 150 × 100 unit SVG holds two toothed wheels drawn with the same module (tooth size): a large one with 12 teeth (pitch radius 30, centered at 55,50) and a small one with 8 teeth (pitch radius 20, centered at 105,50). The centers are 50 units apart, the sum of both radii: a tooth of the large wheel sits in a gap of the small one, like a real mechanism. The center hole is part of the path (fill-rule="evenodd"): it is a real hole, the page background shows through.
Each wheel sits in a .cogs-gear group animated by @keyframes cogsTurn (rotation from 0 to 360°). transform-box: fill-box and transform-origin: center put the axis at the center of the wheel itself: it spins in place. The large wheel turns once in 4 s; the small one turns the other way (animation-direction: reverse) in 4 s × 8/12, i.e. 1.5 times faster, which keeps the teeth engaged at all times.
Everything goes through variables: --cogs-speed sets both speeds at once, --cogs-state (running or paused) pauses without losing the position, so the wheels can turn on hover or while a task runs. No JavaScript; classes and animation prefixed cogs.
Accessibility
- Decorative: the SVG has
aria-hidden="true"andfocusable="false". Used as an activity indicator, pair it with text in arole="status"area ("Syncing…", "Up to date"): that text informs, not the rotation. - Reduced motion: under
prefers-reduced-motion: reduce, both wheels stay still, meshed, in their starting position. - Hover and keyboard: the effect captures nothing by itself. For an icon that turns on hover, set
--cogs-state:pausedon the button andrunningon:hoverand:focus-within, as in the third example: keyboard users get the same feedback as mouse users. - Speed: a slow, even rotation, with no blinking.
Browser compatibility
CSS animations on SVG groups, transform-box: fill-box, CSS variables in animation and animation-play-state, calc() on a duration, prefers-reduced-motion. Measured in Chromium, Firefox and WebKit (Playwright): 0 errors; center of each wheel tracked for 2 s: 0 px movement in all 3 engines; teeth never overlapping across 80 instants of a turn; frozen frames under reduced motion; two independent copies.
Without transform-box (browsers older than those listed), the wheels would turn around the corner of the drawing: they stay visible but the motion is wrong. Without CSS animations, they stay still and meshed.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0607-root">
<div class="cogs-stage">
<svg class="cogs-svg" viewBox="0 0 150 100" aria-hidden="true" focusable="false">
<g transform="translate(55 50)">
<g class="cogs-gear cogs-gear--big">
<path fill-rule="evenodd" d="…"/>
</g>
</g>
<g transform="translate(105 50)">
<g class="cogs-gear cogs-gear--small">
<path fill-rule="evenodd" d="…"/>
</g>
</g>
</svg>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Size (CSS — --cogs-w / --cogs-h) |
150px / 100px | Drawing size; the 150 × 100 frame keeps its proportions. 100% / auto follows the parent's width. |
Colors (CSS — --cogs-a / --cogs-b) |
#6366f1 / #8b5cf6 | Large wheel and small wheel. The holes are transparent: no background color to match. |
Speed (CSS — --cogs-speed) |
4s | One turn of the large wheel; the small one automatically follows at 2/3 of that time to stay meshed. |
Pause / play (CSS — --cogs-state) |
running | paused freezes both wheels where they are, running restarts them. Set it on the root or on a parent (button, status bar). |
Block background and height (CSS — --cogs-page, --cogs-root-h) |
#0a0a0f / 300px | transparent and 0px to use the gears as an icon or illustration in your layout. |
FAQ
transform-box: fill-box: the measured center does not move by a single pixel in Chrome, Firefox and Safari (WebKit), and the teeth stay meshed without overlapping.--cogs-speed × small teeth / large teeth, otherwise the teeth pass through each other.--cogs-state: paused on a parent, switch it to running when the task starts and back to paused when it ends: el.style.setProperty('--cogs-state', 'running'). The wheels resume from where they stopped, still meshed.