Skip to content
animorio
IT
Back to the library

The rubber-stamp logo

Intro, SVG, TextGSAPAdvancedCompressed weight: Demo 17 KBGSAP 28 KBFreeDownload the zip

A logo intro with a wooden rubber stamp: it dips into the ink pad, winds up and prints with a blow that bends the sheet and splashes ink; the logo stays printed with a real ink texture, which dries. In the Playground you type your own name or upload your own logo.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The rubber-stamp logo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/familjen-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="scena">
    <!-- the stage: the script measures it, builds the sheet, the ink pad and the stamp's block, and prints the logo -->
    <svg class="palco" data-stage role="img" aria-label="Pressly" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <!-- the grain of the paper: a little tile of specks and fibres the script paints on a canvas -->
        <pattern id="grain" patternUnits="userSpaceOnUse" width="180" height="180">
          <image width="180" height="180" data-grain-image />
        </pattern>
        <!-- the ink texture: blotches of more and less ink, and a few pin holes where the rubber did not touch -->
        <filter id="ink-noise" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB" data-noise>
          <feTurbulence type="fractalNoise" numOctaves="3" result="blot" data-noise-blot />
          <feColorMatrix in="blot" type="matrix" result="soft" data-noise-soft />
          <feTurbulence type="fractalNoise" numOctaves="2" result="dust" data-noise-dust />
          <feColorMatrix in="dust" type="matrix" result="holes" data-noise-holes />
          <feComposite in="soft" in2="holes" operator="arithmetic" k1="1" k2="0" k3="0" k4="0" />
        </filter>
        <mask id="ink-mask" maskUnits="userSpaceOnUse" data-ink-mask>
          <rect fill="#fff" filter="url(#ink-noise)" data-ink-mask-rect />
        </mask>
        <!-- the rough edges of a rubber print: the shapes pushed about by a fine noise, a bleed when there is a lot of ink -->
        <filter id="ink-rough" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB" data-rough>
          <feMorphology operator="dilate" radius="0" result="fat" data-rough-fat />
          <feTurbulence type="fractalNoise" numOctaves="2" result="wobble" data-rough-turb />
          <feDisplacementMap in="fat" in2="wobble" xChannelSelector="R" yChannelSelector="G" data-rough-disp />
        </filter>
        <!-- the fresh-ink shine shows only on the print -->
        <mask id="print-alpha" maskUnits="userSpaceOnUse" style="mask-type: alpha" data-print-alpha>
          <use href="#print-dry" />
        </mask>
        <linearGradient id="gloss-grad" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0" class="lucido-stop lucido-stop--via" />
          <stop offset="0.5" class="lucido-stop" />
          <stop offset="1" class="lucido-stop lucido-stop--via" />
        </linearGradient>
        <!-- soft shadows -->
        <filter id="soft" x="-30%" y="-80%" width="160%" height="260%" data-soft>
          <feGaussianBlur stdDeviation="6" data-soft-blur />
        </filter>
        <!-- the turned handle, to keep its shade, grain and grooves inside it -->
        <clipPath id="handle-clip">
          <path d="M-54 0L54 0L54 -10C54 -13 51 -14 46 -15C34 -17 26 -24 24 -34C21 -50 18 -66 19 -84C24 -86 28 -88 28 -91L28 -98C28 -101 24 -103 22 -106C36 -112 48 -126 48 -146C48 -170 26 -188 0 -188C-26 -188 -48 -170 -48 -146C-48 -126 -36 -112 -22 -106C-24 -103 -28 -101 -28 -98L-28 -91C-28 -88 -24 -86 -19 -84C-18 -66 -21 -50 -24 -34C-26 -24 -34 -17 -46 -15C-51 -14 -54 -13 -54 -10Z" />
        </clipPath>
      </defs>

      <g data-world>
        <!-- the sheet of paper, with the print on it -->
        <g data-paper-place>
          <g data-paper>
            <path class="foglio-ombra foglio-ombra--larga" data-paper-shadow-2 />
            <path class="foglio-ombra" data-paper-shadow />
            <path class="foglio" data-sheet />
            <path class="foglio-grana" fill="url(#grain)" data-sheet-grain />
            <path class="foglio-bordo" data-sheet-edge />
            <g data-rings></g>
            <g filter="url(#ink-rough)" mask="url(#ink-mask)" data-print-holder>
              <g id="print-dry" class="stampa" data-print-dry></g>
              <g class="stampa-bagnata" data-print-wet></g>
              <g class="stampa" data-specks></g>
            </g>
            <g mask="url(#print-alpha)"><path fill="url(#gloss-grad)" data-gloss /></g>
          </g>
        </g>

        <!-- the ink pad: a tin with its lid open and the felt soaked in ink -->
        <g data-pad-place>
          <g data-pad></g>
        </g>

        <!-- the stamp and its shadow; the point in the middle of the rubber's bottom edge is 0 0 -->
        <g data-stamp-all>
          <rect class="timbro-ombra" filter="url(#soft)" data-stamp-shadow />
          <g data-stamp-place>
            <!-- the face of the rubber, with the logo the wrong way round: seen only when the stamp tips it towards us -->
            <g data-face></g>
            <g data-rubber></g>
            <g data-upper>
              <g data-block></g>
              <!-- the turned wooden handle: drawn once, standing on the top of the block -->
              <g data-handle-place>
                <g data-handle>
                  <ellipse class="appoggio" cx="0" cy="1" rx="62" ry="9" />
                  <path class="manico" d="M-54 0L54 0L54 -10C54 -13 51 -14 46 -15C34 -17 26 -24 24 -34C21 -50 18 -66 19 -84C24 -86 28 -88 28 -91L28 -98C28 -101 24 -103 22 -106C36 -112 48 -126 48 -146C48 -170 26 -188 0 -188C-26 -188 -48 -170 -48 -146C-48 -126 -36 -112 -22 -106C-24 -103 -28 -101 -28 -98L-28 -91C-28 -88 -24 -86 -19 -84C-18 -66 -21 -50 -24 -34C-26 -24 -34 -17 -46 -15C-51 -14 -54 -13 -54 -10Z" />
                  <g clip-path="url(#handle-clip)">
                    <path class="manico-ombra" d="M24 6C36 -18 28 -52 15 -84C30 -100 42 -122 32 -152C27 -168 20 -180 8 -194L80 -194L80 6Z" />
                    <path class="manico-vena" d="M-6 -118C-13 -138 -11 -160 2 -180M10 -120C8 -140 14 -160 22 -176M-28 -136C-34 -152 -30 -166 -20 -178M-6 -40C-8 -56 -7 -70 -5 -82M6 -38C4 -54 6 -68 8 -80" />
                    <path class="manico-riga" d="M-56 -10Q0 -3 56 -10M-30 -91Q0 -86 30 -91M-30 -98Q0 -93 30 -98M-23 -106Q0 -102 23 -106" />
                    <path class="manico-lucido" d="M-36 -146C-36 -162 -27 -174 -13 -179M-15 -42C-14 -56 -13 -68 -13 -80M-46 -5L-26 -5M-22 -94L-12 -94" />
                    <ellipse class="manico-riflesso" cx="-22" cy="-160" rx="7" ry="11" transform="rotate(28 -22 -160)" />
                  </g>
                  <path class="manico-bordo" d="M-54 0L54 0L54 -10C54 -13 51 -14 46 -15C34 -17 26 -24 24 -34C21 -50 18 -66 19 -84C24 -86 28 -88 28 -91L28 -98C28 -101 24 -103 22 -106C36 -112 48 -126 48 -146C48 -170 26 -188 0 -188C-26 -188 -48 -170 -48 -146C-48 -126 -36 -112 -22 -106C-24 -103 -28 -101 -28 -98L-28 -91C-28 -88 -24 -86 -19 -84C-18 -66 -21 -50 -24 -34C-26 -24 -34 -17 -46 -15C-51 -14 -54 -13 -54 -10Z" />
                </g>
              </g>
              <!-- its face on the front of the block: cheeks, eyes with lids, brows, a mouth -->
              <g data-eyes-place>
                <g data-eyes>
                  <ellipse class="guancia" cx="-52" cy="17" rx="10" ry="6" data-cheek />
                  <ellipse class="guancia" cx="52" cy="17" rx="10" ry="6" data-cheek />
                  <ellipse class="occhio" cx="-26" cy="0" rx="14" ry="16" />
                  <ellipse class="occhio" cx="26" cy="0" rx="14" ry="16" />
                  <g data-pupils>
                    <circle class="iride" cx="-26" cy="2" r="9" />
                    <circle class="pupilla" cx="-26" cy="2" r="5" />
                    <circle class="riflesso" cx="-29.5" cy="-1.6" r="2.6" />
                    <circle class="iride" cx="26" cy="2" r="9" />
                    <circle class="pupilla" cx="26" cy="2" r="5" />
                    <circle class="riflesso" cx="22.5" cy="-1.6" r="2.6" />
                  </g>
                  <g data-lids>
                    <ellipse class="palpebra" cx="-26" cy="0" rx="15.4" ry="17.4" />
                    <ellipse class="palpebra" cx="26" cy="0" rx="15.4" ry="17.4" />
                  </g>
                  <path class="ciglio" d="M-41 -25Q-27 -32 -12 -26" data-brow-l />
                  <path class="ciglio" d="M12 -26Q27 -32 41 -25" data-brow-r />
                  <path class="bocca" d="M-10 22Q0 31 10 22" data-smile />
                  <ellipse class="bocca-aperta" cx="0" cy="26" rx="5.5" ry="6.5" data-oh />
                </g>
              </g>
            </g>
          </g>
        </g>

        <g data-fx></g>
      </g>
    </svg>
    <p class="nota">Click or tap to see it again</p>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

39 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The rubber-stamp logo” from Animorio and fit it into my site

What it does

On a sand-coloured desk a sheet of grainy paper slides in from the left and settles with a little flutter; from the right comes an ink pad, a small tin with its lid open and blue felt inside. From above drops a big wooden stamp, with a shiny turned handle, grain and a knot in its block and a little face on the front: it comes down tilted and shows its red rubber, where the logo of a made-up brand, Pressly, is cut the wrong way round. It looks at the pad, blinks and dips into it once or twice, rocking: the felt squashes, a wet mark stays and drops of ink jump out. It lifts, tips to check its rubber, blue now, the pad leaves, and it winds up high, frowning and trembling with the effort. Then the blow: the stamp squashes, the sheet bends and quivers, a shock wave runs across the paper, streaks and drops of ink splash out at the edges and the whole scene shakes. The stamp leans on it a moment longer, then comes away with a little suction (it stretches, the sheet lifts a touch and drops back) and underneath is the logo, printed with rough edges, heavier and lighter patches and a few pin holes where the rubber did not touch. A glint of fresh ink passes over it and the blue gets lighter as it dries. The stamp takes a happy bow and flies away. The logo stays there, the sheet breathing gently and a glint now and then; after a few seconds the sheet slides off to the right and a new one comes in, with a print that is a little different every time. A click restarts it. A picture can take the place of the word: it is cut into the rubber and printed in the ink colour, or in its own colours with the same texture.

How it works

One size

The stage is an SVG whose viewBox is as big as the window, so one unit is one pixel. The letters are measured on a canvas in the same typeface (measureText, actualBoundingBoxAscent for the true height) and everything is counted in letter heights: the rubber is as wide as the logo plus a margin, the block as tall as the logo plus half a letter (so that, squashed, it still covers it), the sheet a little wider, the pad under the sheet. The size is chosen so that the stamp hovering over the print and the pad below both fit; on a tall screen the sheet turns upright.

The ink texture

The print is a group with a filter and a mask. The filter pushes its edges about with a feDisplacementMap driven by a fine feTurbulence (and, with a lot of ink, fattens them with feMorphology). The mask is a white rectangle with a noise filter: a broad feTurbulence, brought between grey and white with feColorMatrix, gives the heavier and lighter patches; a dense, thresholded one gives the pin holes where the rubber did not touch; feComposite arithmetic multiplies them. Every sheet gets new seeds, so every print is different. On top sits a darker copy, the wet ink, which slowly fades: that is the drying.

The stamp

The turned handle is drawn once in the page, with its shade, grain, rings and highlights clipped inside its outline; the block, the rubber and the pad are built by the script, because they depend on the length of the logo. Place, height above the surface, lean, squash and how far the stamp is tipped towards us live in one object, written as a transform attribute: the origin is the middle of the rubber's edge, so the squash starts there. When it tips, the upper part gets shorter and the face of the rubber appears below it, a trapezoid with the logo mirrored by scale(-1 1). The handle sways with skewX around its base, so it never comes off the block.

The blow

The stamp falls with power4.in, squashes for five hundredths of a second and springs back with elastic.out. At the same instant the print appears (hidden under the block), the sheet squashes, its outline (a path of four quadratic curves) bends and quivers through a series of dying curves, two rounded rectangles spread out as a shock wave and the splashes fly outwards from the edges of the rubber, stopped inside the sheet. The suction is the opposite: the stamp stretches while it stays stuck, the sheet bends upwards, then everything lets go with power3.out.

Direction and your logo

The whole intro is one timeline, with the labels start, arrive, ink, load, press, lift, reveal, bow, fly, rest and timeScale for the speed. At rest the sheet breathes and a glint passes over the print, shown only on the letters by an alpha mask holding a use of the print. If IMAGE holds an address, the logo becomes that picture: a filter turns the dark and coloured parts into ink and leaves the light parts and the transparent background as paper, and the same filter, in rubber colour, cuts it into the stamp's face. In the Playground, next to the field, there is a button to upload a file, which goes into the code as a data: address.

Values to change

NameFileDefaultWhat it changes
NAMEscript.js'Pressly'The logo as a word: it is cut the wrong way round in the rubber and printed on the sheet.
IMAGEscript.js''The logo as a picture: the address of a file, or upload it from your computer. If there is one, it takes the place of the word.
INKscript.js1How much ink: 0.4 gives a dry, patchy print, 1.5 a full one that bleeds a little.
PRESSscript.js1How hard the blow is: how much the stamp squashes and the scene shakes, the sheet bends and the shock wave runs.
SPLATTERscript.js1How many ink splashes come out at the edges and how far they go (0 = a clean print).
TINTscript.jstrueA picture is printed in the ink colour; with false it keeps its own colours, with the ink texture.
SPEEDscript.js1The speed of the whole animation: 2 is twice as fast, 0.5 half as fast.
RESTscript.js4How many seconds the printed logo stays on show before the sheet slides away.
LOOPscript.jstrueWhether the sheet slides away and a new one is printed. With false the logo stays, the sheet breathing and glinting.

Try them in the Playground

When to use it

To present a logo with a gesture that smells of paper and offices: the opening of a print shop's site, a design studio, a stationery shop or a craft brand, a preview for a client, a loading screen, a social post. It works with a written wordmark and with a picture.

On phones

The same animation with the sizes adapted: on a tall screen the sheet stands upright, like a letter, and the logo is almost as wide as the screen. A tap restarts it.

With reduced motion

Nothing moves: the sheet and the pad fade in, then the stamp on the pad; stamp and pad fade away and the printed logo fades in in their place. If the animation starts again, the sheet leaves with a fade too.

Pitfalls

  • The handle, turned around its base to make it sway, came off the block on one side: with skewX the base stays flat and seated and only the top moves.
  • The face of the rubber, squashed to zero when the stamp is upright, still left its outline (vector-effect non-scaling-stroke) as a line wider than the stamp: it is hidden when the tilt is almost zero.
  • The block must cover the logo even when the stamp squashes, or the print shows before the stamp lifts: it is as tall as the logo plus half a letter, and the squash has a limit.
  • The stamp's shadow, far above, stayed as a small blurred smudge at the edge of the screen after the flight: its opacity goes down to zero, not to a minimum.
  • A picture with a transparent background and white details, tinted by its alpha alone, turned into a solid shape: the filter multiplies the alpha by how dark or coloured the pixel is, so the whites stay paper.
  • The number of dips into the pad and the side the stamp comes from change on every round, and the timing with them: the first print is always the same (two dips, from the right), so the poster frame always falls on the stamp just lifted.

Libraries

GSAP 3.15.0, Familjen Grotesk (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 17 KB; the libraries: GSAP 28 KB.

Testing

Tested on 10 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: To do

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All animations