Skip to content
CoDdleCoDoodle home
Animal DoodlesBeginnerSVG + CSS

Baby Hippo

A chubby baby hippo bouncing in its very own puddle — huge snout, sleepy eyes, two tiny tooth nubs, and absolutely zero chill.

← you, in 25 minutes.

what you'll build

Here's the plan

A chubby baby hippo standing in a shallow puddle — one big body-blob, a snout almost as wide as the body, sleepy eyes, two tooth nubs, and stubby legs — bouncing with proper squash-and-stretch while its ears flick on opposite beats and droplets pop on every hop. All of it is hand-placed SVG shapes plus a handful of transform-only CSS animations.

You’ll learn

  • Building a chunky character from just two masses: a body blob and one oversized snout ellipse
  • Squash-and-stretch: pairing translateY with an opposing scaleX/scaleY, anchored at the feet so the ground stays put
  • When to use transform-box: fill-box (droplets, eyes) versus a hand-picked pixel origin (ear bases, feet)
  • Syncing prop animations to the main loop — the droplets pop on a 2.6s cycle so every splash lands on the bounce beat
Skill level
Beginner
Time
~25 min
Tools
Just a browser
Code type
SVG + CSS
Lines
~74

the steps

Build it, one change at a time

  1. Set the stage

    Warm paper, a pale blue puddle where the hippo will land, and a soft shadow INSIDE the puddle — the shadow stays outside the character group so it can flatten while the body bounces. Two droplets and two sparkles wait in the wings; they'll pop later.

    <rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/>
    <!-- the puddle is the ground — the hippo's feet will land at y=176 -->
    <ellipse class="md-puddle" cx="100" cy="176" rx="54" ry="10" fill="#BFD9F2" stroke="#2B3A55" stroke-width="3"/>
    <!-- shadow lives OUTSIDE the character group so it can squash independently -->
    <ellipse class="md-shadow" cx="100" cy="176" rx="38" ry="6.5" fill="#2B3A55" opacity="0.14" stroke="none"/>
    <path class="md-ripple" d="M64 180 Q82 184 102 183" fill="none" stroke="#FFFDF7" stroke-width="2.5" stroke-linecap="round" opacity="0.6"/>
    <!-- droplets: drawn now, animated later -->
    <path class="md-drop md-drop1" d="M38 118 C33 126 33 132 38 134 C43 132 43 126 38 118 Z" fill="#BFD9F2" stroke="#2B3A55" stroke-width="2.5" stroke-linejoin="round"/>
    <path class="md-drop md-drop2" d="M164 96 C160 102 160 107 164 109 C168 107 168 102 164 96 Z" fill="#BFD9F2" stroke="none"/>
    <path class="md-spark md-spark1" d="M168 132 L171 140 L168 148 L165 140 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
    <path class="md-spark md-spark2" d="M32 60 L34.5 66 L32 72 L29.5 66 Z" fill="#FF6B5E" stroke="none"/>
    

    an empty puddle, patiently waiting for its hippo.

  2. Build the chubby silhouette

    Back-to-front painting does all the work: each tiny ear goes in its OWN group (they'll flick separately later), then the four stubby legs, then the big body blob overlapping the leg tops and ear bases — and finally the snout, which is nearly as wide as the body. That oversized snout IS the character.

    <g class="md-all">
      <!-- each ear is its own group so it can flick on its own beat -->
      <g class="md-ear md-ear-l">
        <circle cx="68" cy="68" r="10" fill="#C9A9B4" stroke="#2B3A55" stroke-width="4"/>
      </g>
      <g class="md-ear md-ear-r">
        <circle cx="132" cy="68" r="10" fill="#C9A9B4" stroke="#2B3A55" stroke-width="4"/>
      </g>
      <!-- back legs are a shade darker so they read as further away -->
      <g class="md-legs" stroke="#2B3A55" stroke-width="4" stroke-linejoin="round">
        <rect x="52" y="140" width="18" height="36" rx="9" fill="#B598A3"/>
        <rect x="130" y="140" width="18" height="36" rx="9" fill="#B598A3"/>
        <rect x="66" y="138" width="22" height="38" rx="10" fill="#C9A9B4"/>
        <rect x="112" y="138" width="22" height="38" rx="10" fill="#C9A9B4"/>
      </g>
      <!-- body goes over the leg tops and ear bases so it reads as one mass -->
      <path class="md-body" d="M48 116 C48 84 70 64 100 64 C130 64 152 84 152 116 C152 142 130 158 100 158 C70 158 48 142 48 116 Z" fill="#C9A9B4" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
      <!-- the snout: almost as wide as the body — this is what makes it a hippo -->
      <ellipse class="md-snout" cx="100" cy="124" rx="36" ry="22" fill="#E3C6CE" stroke="#2B3A55" stroke-width="4"/>
    </g>
    

    a faceless pink-grey blob — already 80% hippo.

  3. Add the sleepy face

    Pink inner ears go INSIDE each ear's group (so they ride along when the ears flick), then the wet sheen, belly peek, and blush. The face is all small marks: beady navy eyes with heavy lids for that just-woke-up look, two nostril ovals, a smile curve — and two tiny tooth nubs hanging from it. Toe lines at the leg bottoms finish the feet.

    <!-- inner ears live inside each md-ear group so they flick too -->
    <circle cx="68" cy="66.5" r="4" fill="#FF8FA3" stroke="none"/>
    <!-- a wet sheen along the back, a belly peek, and two blush spots -->
    <path class="md-sheen" d="M66 82 Q76 68 94 65" fill="none" stroke="#FFFDF7" stroke-width="4" stroke-linecap="round" opacity="0.55"/>
    <ellipse class="md-belly" cx="100" cy="152.5" rx="16" ry="4" fill="#E3C6CE" opacity="0.9" stroke="none"/>
    <ellipse cx="60" cy="105" rx="6.5" ry="4.5" fill="#FF8FA3" opacity="0.75" stroke="none"/>
    <g class="md-face">
      <!-- each eye is a group: pupil + glint + a heavy sleepy lid -->
      <g class="md-eye md-eye-l">
        <circle cx="76" cy="94" r="4.5" fill="#2B3A55" stroke="none"/>
        <circle cx="74.5" cy="92.5" r="1.5" fill="#FFFDF7" stroke="none"/>
        <path d="M70.5 91 Q76 88.5 81.5 91" fill="none" stroke="#2B3A55" stroke-width="2.5" stroke-linecap="round"/>
      </g>
      <!-- nostrils, a smile, and two tooth nubs hanging from the smile line -->
      <ellipse cx="87" cy="115" rx="3.8" ry="5.2" fill="#2B3A55" stroke="none"/>
      <ellipse cx="113" cy="115" rx="3.8" ry="5.2" fill="#2B3A55" stroke="none"/>
      <path d="M84 132 Q100 141 116 132" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round"/>
      <rect x="91" y="134.5" width="6" height="7" rx="2.4" fill="#FFFDF7" stroke="#2B3A55" stroke-width="2"/>
      <rect x="103" y="134.5" width="6" height="7" rx="2.4" fill="#FFFDF7" stroke="#2B3A55" stroke-width="2"/>
    </g>
    <!-- little toe lines at the bottom of each front leg -->
    <g class="md-toes" fill="none" stroke="#2B3A55" stroke-width="2.5" stroke-linecap="round">
      <path d="M73 175 v-6"/>
      <path d="M81 175 v-6"/>
      <path d="M119 175 v-6"/>
      <path d="M127 175 v-6"/>
    </g>
    

    the finished static hippo — sleepy, blushing, two teeth.

  4. Boing, flick, splash

    The whole md-all group squashes and stretches from a point at its FEET (100px 176px) — squashed wide on the ground, stretched tall at the top of the hop — while the shadow flattens underneath. Both ears share one flick animation; the right ear starts half a cycle late via a negative delay. The droplets loop on the same 2.6s as the bounce, so every splash lands on the beat, and it all sits inside a prefers-reduced-motion guard.

    @media (prefers-reduced-motion: no-preference) {
      /* origin at the FEET so the squash presses into the puddle */
      .md .md-all { transform-origin: 100px 176px; animation: md-boing 2.6s ease-in-out infinite; }
      .md .md-shadow { transform-box: fill-box; transform-origin: center; animation: md-shadow 2.6s ease-in-out infinite; }
      /* one flick animation, two ears — the right one runs half a cycle late */
      .md .md-ear-l { transform-origin: 68px 78px; animation: md-ear-flick 2.2s ease-in-out infinite; }
      .md .md-ear-r { transform-origin: 132px 78px; animation: md-ear-flick 2.2s ease-in-out -1.1s infinite; }
      .md .md-eye { transform-box: fill-box; transform-origin: center; animation: md-blink 5.2s ease-in-out infinite; }
      /* droplets share the bounce's 2.6s so splashes land on the beat */
      .md .md-drop { transform-box: fill-box; transform-origin: center; }
      .md .md-drop1 { animation: md-splash 2.6s ease-out infinite; }
      .md .md-drop2 { animation: md-splash 2.6s ease-out -1.3s infinite; }
      .md .md-spark { transform-box: fill-box; transform-origin: center; }
      .md .md-spark1 { animation: md-twinkle 2.8s ease-in-out infinite; }
      .md .md-spark2 { animation: md-twinkle 2.8s ease-in-out 0.9s infinite; }
    }
    @keyframes md-boing { 0%, 100% { transform: translateY(0) scale(1.07, 0.93); } 42% { transform: translateY(-9px) scale(0.95, 1.06); } 58% { transform: translateY(-7px) scale(1, 1); } }
    @keyframes md-shadow { 0%, 100% { transform: scaleX(1); opacity: 0.14; } 50% { transform: scaleX(0.78); opacity: 0.08; } }
    @keyframes md-ear-flick { 0%, 55%, 100% { transform: rotate(0deg); } 65% { transform: rotate(-16deg); } 78% { transform: rotate(8deg); } 88% { transform: rotate(-3deg); } }
    @keyframes md-blink { 0%, 42%, 48%, 100% { transform: scaleY(1); } 45% { transform: scaleY(0.08); } }
    @keyframes md-splash { 0% { opacity: 0; transform: translateY(10px) scale(0.5); } 35% { opacity: 1; transform: translateY(-8px) scale(1); } 70% { opacity: 0.9; transform: translateY(0) scale(1); } 100% { opacity: 0; transform: translateY(12px) scale(0.7); } }
    @keyframes md-twinkle { 0%, 100% { opacity: 0.35; transform: scale(0.8); } 45% { opacity: 1; transform: scale(1.15); } }
    

    bouncing, flicking, splashing — a fully operational baby hippo.

the complete code

Everything, in one place

Skipped straight to the end? Welcome. Copy the whole thing, download it, or open it in the Playground to start remixing.

<svg class="md" viewBox="0 0 200 200" width="200" height="200" role="img" aria-label="A chubby baby hippo doodle bouncing in its puddle with flicking ears, popping water droplets, and an occasional sleepy blink">
  <rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/>
  <ellipse class="md-puddle" cx="100" cy="176" rx="54" ry="10" fill="#BFD9F2" stroke="#2B3A55" stroke-width="3"/>
  <ellipse class="md-shadow" cx="100" cy="176" rx="38" ry="6.5" fill="#2B3A55" opacity="0.14" stroke="none"/>
  <path class="md-ripple" d="M64 180 Q82 184 102 183" fill="none" stroke="#FFFDF7" stroke-width="2.5" stroke-linecap="round" opacity="0.6"/>
  <path class="md-drop md-drop1" d="M38 118 C33 126 33 132 38 134 C43 132 43 126 38 118 Z" fill="#BFD9F2" stroke="#2B3A55" stroke-width="2.5" stroke-linejoin="round"/>
  <path class="md-drop md-drop2" d="M164 96 C160 102 160 107 164 109 C168 107 168 102 164 96 Z" fill="#BFD9F2" stroke="none"/>
  <path class="md-spark md-spark1" d="M168 132 L171 140 L168 148 L165 140 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
  <path class="md-spark md-spark2" d="M32 60 L34.5 66 L32 72 L29.5 66 Z" fill="#FF6B5E" stroke="none"/>
  <g class="md-all">
    <g class="md-ear md-ear-l">
      <circle cx="68" cy="68" r="10" fill="#C9A9B4" stroke="#2B3A55" stroke-width="4"/>
      <circle cx="68" cy="66.5" r="4" fill="#FF8FA3" stroke="none"/>
    </g>
    <g class="md-ear md-ear-r">
      <circle cx="132" cy="68" r="10" fill="#C9A9B4" stroke="#2B3A55" stroke-width="4"/>
      <circle cx="132" cy="66.5" r="4" fill="#FF8FA3" stroke="none"/>
    </g>
    <g class="md-legs" stroke="#2B3A55" stroke-width="4" stroke-linejoin="round">
      <rect x="52" y="140" width="18" height="36" rx="9" fill="#B598A3"/>
      <rect x="130" y="140" width="18" height="36" rx="9" fill="#B598A3"/>
      <rect x="66" y="138" width="22" height="38" rx="10" fill="#C9A9B4"/>
      <rect x="112" y="138" width="22" height="38" rx="10" fill="#C9A9B4"/>
    </g>
    <path class="md-body" d="M48 116 C48 84 70 64 100 64 C130 64 152 84 152 116 C152 142 130 158 100 158 C70 158 48 142 48 116 Z" fill="#C9A9B4" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
    <ellipse class="md-snout" cx="100" cy="124" rx="36" ry="22" fill="#E3C6CE" stroke="#2B3A55" stroke-width="4"/>
    <path class="md-sheen" d="M66 82 Q76 68 94 65" fill="none" stroke="#FFFDF7" stroke-width="4" stroke-linecap="round" opacity="0.55"/>
    <ellipse class="md-belly" cx="100" cy="152.5" rx="16" ry="4" fill="#E3C6CE" opacity="0.9" stroke="none"/>
    <ellipse cx="60" cy="105" rx="6.5" ry="4.5" fill="#FF8FA3" opacity="0.75" stroke="none"/>
    <ellipse cx="140" cy="105" rx="6.5" ry="4.5" fill="#FF8FA3" opacity="0.75" stroke="none"/>
    <g class="md-face">
      <g class="md-eye md-eye-l">
        <circle cx="76" cy="94" r="4.5" fill="#2B3A55" stroke="none"/>
        <circle cx="74.5" cy="92.5" r="1.5" fill="#FFFDF7" stroke="none"/>
        <path d="M70.5 91 Q76 88.5 81.5 91" fill="none" stroke="#2B3A55" stroke-width="2.5" stroke-linecap="round"/>
      </g>
      <g class="md-eye md-eye-r">
        <circle cx="124" cy="94" r="4.5" fill="#2B3A55" stroke="none"/>
        <circle cx="122.5" cy="92.5" r="1.5" fill="#FFFDF7" stroke="none"/>
        <path d="M118.5 91 Q124 88.5 129.5 91" fill="none" stroke="#2B3A55" stroke-width="2.5" stroke-linecap="round"/>
      </g>
      <ellipse cx="87" cy="115" rx="3.8" ry="5.2" fill="#2B3A55" stroke="none"/>
      <ellipse cx="113" cy="115" rx="3.8" ry="5.2" fill="#2B3A55" stroke="none"/>
      <path d="M84 132 Q100 141 116 132" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round"/>
      <rect x="91" y="134.5" width="6" height="7" rx="2.4" fill="#FFFDF7" stroke="#2B3A55" stroke-width="2"/>
      <rect x="103" y="134.5" width="6" height="7" rx="2.4" fill="#FFFDF7" stroke="#2B3A55" stroke-width="2"/>
    </g>
    <g class="md-toes" fill="none" stroke="#2B3A55" stroke-width="2.5" stroke-linecap="round">
      <path d="M73 175 v-6"/>
      <path d="M81 175 v-6"/>
      <path d="M119 175 v-6"/>
      <path d="M127 175 v-6"/>
    </g>
  </g>
</svg>
Open in Playground

tips & gotchas

Mistakes we actually made

My first snout was a modest little oval and the whole thing read 'generic blob with dots'. The snout has to be almost as wide as the body — I kept nudging rx up until it felt slightly ridiculous, and THAT'S when it became a hippo.

transform-box: fill-box is perfect for parts that scale or fade in place (droplets, eyes), but the ears need a hand-picked pixel origin at their BASE — with fill-box centers they spin like propellers instead of flicking.

Squash and stretch should roughly trade off: I pair scale(1.07, 0.93) on the ground with scale(0.95, 1.06) at the top of the hop, so the hippo keeps its volume instead of inflating and deflating.

Give the droplets the SAME duration as the bounce (2.6s) and offset them with delays. When I used a 'nicer' 3s loop, the splashes drifted out of sync and the whole scene felt seasick.

make it yours

Remix it

  • Yawn modeeasy

    Swap the smile path for a big open 'o' ellipse (navy fill, tooth nubs on its top edge) — instant mid-yawn hippo.

  • Mud batheasy

    Recolour the puddle to a warm mud brown (try #C9A97A) and scatter a few tiny mud-speckle ellipses on the snout and belly.

  • Rain showermedium

    Add four more md-drop teardrops near the top of the frame and give each a falling translateY animation with staggered delays.

  • Puddle palmedium

    Wrap a second copy of md-all in a group with transform="translate(55 55) scale(0.45)" — a tiny sibling bouncing on its own delay.

challenge extension

Split the snout into an upper half and a jaw group hinged at the back corners, then chomp: rotate the jaw open a few degrees on every second bounce, timed so the mouth is widest at the top of the hop. Squash-and-stretch, but make it snacky.