Labubu
A chibi Labubu sticker with tall wiggling ears, big round eyes, and that famous nine-tooth grin — bouncing gently like it just spotted a blind box.
This coded drawing is a fan-art practice example created for educational purposes only. Labubu is a copyrighted character created by artist Kasing Lung and produced under license by POP MART. CoDoodle is not affiliated with, endorsed by, or sponsored by Kasing Lung or POP MART.
← you, in 30 minutes.
what you'll build
Here's the plan
A chibi Labubu sticker — tall bunny-like ears, a round furry head, huge eyes, and the famous nine-tooth grin — bouncing gently on warm cream paper. The ears are drawn as their OWN groups so they can wiggle on opposite beats, and the whole character squashes and stretches like it's made of plush.
You’ll learn
- Building a character back-to-front: ears first, then body, then the head overlapping the ear bases
- Keeping animatable parts (each ear, each eye) in their own groups so they can move independently later
- Drawing a toothy grin as one dark mouth shape with a row of small white triangles on top
- Wiggling two ears on opposite beats with one shared animation and a negative animation-delay
- Skill level
- Beginner
- Time
- ~30 min
- Tools
- Just a browser
- Code type
- SVG + CSS
- Lines
- ~64
the steps
Build it, one change at a time
Set the stage
Warm paper, one soft ground shadow where Labubu will land, and a scatter of sparkles and hearts. The shadow stays OUTSIDE the character group so it can flatten against the ground while the body bounces.
<rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/> <ellipse class="lb-shadow" cx="101" cy="176" rx="48" ry="10" fill="#2B3A55" opacity="0.16" stroke="none"/> <path class="lb-spark lb-spark1" d="M31 54 L35 62 L31 70 L27 62 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/> <path class="lb-spark lb-spark2" d="M165 45 L168 52 L165 59 L162 52 Z" fill="#FF6B5E" stroke="none"/> <path class="lb-spark lb-heart1" d="M42 135 C39 130 32 132 33 138 C34 144 43 148 43 148 C43 148 52 142 51 136 C50 131 45 130 42 135 Z" fill="#FF8FA3" stroke="#2B3A55" stroke-width="2.4" stroke-linejoin="round"/> <path class="lb-spark lb-heart2" d="M158 124 C156 120 151 121 151 125 C151 130 158 133 158 133 C158 133 165 130 165 125 C165 121 160 120 158 124 Z" fill="#FFD84D" stroke="none"/>an empty stage: one soft shadow, two sparkles, two hearts.
Build the furry silhouette
Paint order does the work: each tall ear goes in its OWN group (they'll wiggle separately later), then the round body with stubby arms and feet, then the head LAST so it overlaps the ear bases and everything reads as one plush shape. All of it lives inside one lb-all group so the whole character can bounce together.
<g class="lb-all"> <!-- each ear is its own group so it can wiggle on its own beat --> <g class="lb-ear lb-ear-l"> <path d="M67 73 C55 45 48 18 61 11 C76 3 85 35 88 70 Z" fill="#F0DDB8" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> </g> <g class="lb-ear lb-ear-r"> <path d="M118 70 C123 35 133 4 148 13 C161 21 151 48 134 75 Z" fill="#F0DDB8" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> </g> <g class="lb-body" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"> <path d="M72 138 C63 143 58 155 63 164 C68 172 82 166 84 154 Z" fill="#F0DDB8"/> <path d="M128 138 C138 143 143 155 138 164 C133 172 119 166 117 154 Z" fill="#F0DDB8"/> <ellipse cx="100" cy="139" rx="37" ry="35" fill="#F0DDB8"/> <path d="M82 166 C73 168 68 176 73 182 C78 187 94 184 96 174 Z" fill="#F0DDB8"/> <path d="M118 166 C127 168 132 176 127 182 C122 187 106 184 104 174 Z" fill="#F0DDB8"/> </g> <!-- head goes last so it overlaps the ear bases --> <path class="lb-head" d="M61 83 C62 72 70 61 82 56 C86 49 95 47 101 51 C109 47 119 50 123 57 C137 62 144 75 143 90 C150 98 146 113 136 118 C134 132 120 143 101 144 C81 145 66 135 63 121 C52 117 48 102 56 93 C54 88 56 84 61 83 Z" fill="#F0DDB8" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> </g>a faceless plush silhouette — tall ears, round head, stubby everything.
Add the face and the nine-tooth grin
Pink inner-ear shapes go INSIDE each ear's group (so they ride along when the ears wiggle), then the cream belly, a few fur tufts, big white eyes with navy pupils, blush — and the signature grin: one dark mouth shape with exactly nine small white triangles sitting on its top edge.
<!-- inner ears live inside each lb-ear group so they wiggle too --> <path d="M66 63 C58 43 56 25 63 20 C71 17 77 43 80 65 Z" fill="#FF8FA3" stroke="none" opacity="0.9"/> <ellipse cx="100" cy="146" rx="19" ry="22" fill="#FBF0DC" stroke="#2B3A55" stroke-width="3"/> <g class="lb-face"> <g class="lb-eye lb-eye-l"> <ellipse cx="83" cy="91" rx="11" ry="13" fill="#FFFDF7" stroke="#2B3A55" stroke-width="3"/> <circle cx="84" cy="94" r="7" fill="#2B3A55" stroke="none"/> <circle cx="81" cy="88" r="2.2" fill="#FFFDF7" stroke="none"/> </g> <!-- the grin: one dark mouth + nine little white triangles --> <path d="M72 111 C82 132 119 132 129 111 C119 120 82 120 72 111 Z" fill="#2B3A55" stroke="#2B3A55" stroke-width="3" stroke-linejoin="round"/> <g class="lb-teeth" fill="#FFFDF7" stroke="none"> <path d="M78 115 l4 8 l4 -8 Z"/> <path d="M83 118 l4 8 l4 -8 Z"/> <path d="M88 119 l4 8 l4 -8 Z"/> <path d="M94 120 l4 8 l4 -8 Z"/> <path d="M100 120 l4 8 l4 -8 Z"/> <path d="M106 120 l4 8 l4 -8 Z"/> <path d="M112 119 l4 8 l4 -8 Z"/> <path d="M117 118 l4 8 l4 -8 Z"/> <path d="M122 115 l4 8 l4 -8 Z"/> </g> </g>the finished static Labubu, grinning with all nine teeth.
Bounce, wiggle, blink
The whole lb-all group squashes and stretches from its feet while the shadow flattens underneath. Both ears share ONE wiggle animation — the right ear just starts half a cycle late (animation-delay: -1.2s of the 2.4s loop), so they're always on opposite beats. Eyes blink every few seconds, sparkles twinkle staggered, and all of it sits inside @media (prefers-reduced-motion: no-preference) so it freezes politely when motion isn't wanted.
@media (prefers-reduced-motion: no-preference) { .lb .lb-all { transform-origin: 100px 174px; animation: lb-bounce 2.8s ease-in-out infinite; } .lb .lb-shadow { transform-box: fill-box; transform-origin: center; animation: lb-shadow 2.8s ease-in-out infinite; } /* same wiggle on both ears — the right one just starts half a cycle later */ .lb .lb-ear-l { transform-origin: 78px 72px; animation: lb-ear-wiggle 2.4s ease-in-out infinite; } .lb .lb-ear-r { transform-origin: 127px 72px; animation: lb-ear-wiggle 2.4s ease-in-out -1.2s infinite; } .lb .lb-eye { transform-box: fill-box; transform-origin: center; animation: lb-blink 4.8s ease-in-out infinite; } .lb .lb-spark { transform-box: fill-box; transform-origin: center; } .lb .lb-spark1 { animation: lb-twinkle 2.6s ease-in-out infinite; } .lb .lb-spark2 { animation: lb-twinkle 2.6s ease-in-out 0.7s infinite; } .lb .lb-heart1 { animation: lb-twinkle 3s ease-in-out 1.1s infinite; } .lb .lb-heart2 { animation: lb-twinkle 3s ease-in-out 1.8s infinite; } } @keyframes lb-bounce { 0%, 100% { transform: translateY(0) scale(1.04, 0.96); } 50% { transform: translateY(-6px) scale(0.98, 1.04); } } @keyframes lb-shadow { 0%, 100% { transform: scaleX(1); opacity: 0.16; } 50% { transform: scaleX(0.82); opacity: 0.1; } } @keyframes lb-ear-wiggle { 0%, 100% { transform: rotate(0deg); } 35% { transform: rotate(-5deg); } 70% { transform: rotate(4deg); } } @keyframes lb-blink { 0%, 44%, 50%, 92%, 100% { transform: scaleY(1); } 47%, 95% { transform: scaleY(0.08); } } @keyframes lb-twinkle { 0%, 100% { opacity: 0.35; transform: scale(0.82); } 45% { opacity: 1; transform: scale(1.14); } }bouncing, wiggling, blinking — maximum mischief, zero JavaScript.
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="lb" viewBox="0 0 200 200" width="200" height="200" role="img" aria-label="A chibi Labubu fan-art doodle gently bouncing with wiggling ears and twinkling sparkles">
<rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/>
<ellipse class="lb-shadow" cx="101" cy="176" rx="48" ry="10" fill="#2B3A55" opacity="0.16" stroke="none"/>
<path class="lb-spark lb-spark1" d="M31 54 L35 62 L31 70 L27 62 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
<path class="lb-spark lb-spark2" d="M165 45 L168 52 L165 59 L162 52 Z" fill="#FF6B5E" stroke="none"/>
<path class="lb-spark lb-heart1" d="M42 135 C39 130 32 132 33 138 C34 144 43 148 43 148 C43 148 52 142 51 136 C50 131 45 130 42 135 Z" fill="#FF8FA3" stroke="#2B3A55" stroke-width="2.4" stroke-linejoin="round"/>
<path class="lb-spark lb-heart2" d="M158 124 C156 120 151 121 151 125 C151 130 158 133 158 133 C158 133 165 130 165 125 C165 121 160 120 158 124 Z" fill="#FFD84D" stroke="none"/>
<g class="lb-all">
<g class="lb-ear lb-ear-l">
<path d="M67 73 C55 45 48 18 61 11 C76 3 85 35 88 70 Z" fill="#F0DDB8" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M66 63 C58 43 56 25 63 20 C71 17 77 43 80 65 Z" fill="#FF8FA3" stroke="none" opacity="0.9"/>
</g>
<g class="lb-ear lb-ear-r">
<path d="M118 70 C123 35 133 4 148 13 C161 21 151 48 134 75 Z" fill="#F0DDB8" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M127 65 C131 43 139 18 146 22 C153 27 146 47 135 65 Z" fill="#FF8FA3" stroke="none" opacity="0.9"/>
</g>
<g class="lb-body" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M72 138 C63 143 58 155 63 164 C68 172 82 166 84 154 Z" fill="#F0DDB8"/>
<path d="M128 138 C138 143 143 155 138 164 C133 172 119 166 117 154 Z" fill="#F0DDB8"/>
<ellipse cx="100" cy="139" rx="37" ry="35" fill="#F0DDB8"/>
<path d="M82 166 C73 168 68 176 73 182 C78 187 94 184 96 174 Z" fill="#F0DDB8"/>
<path d="M118 166 C127 168 132 176 127 182 C122 187 106 184 104 174 Z" fill="#F0DDB8"/>
</g>
<path class="lb-head" d="M61 83 C62 72 70 61 82 56 C86 49 95 47 101 51 C109 47 119 50 123 57 C137 62 144 75 143 90 C150 98 146 113 136 118 C134 132 120 143 101 144 C81 145 66 135 63 121 C52 117 48 102 56 93 C54 88 56 84 61 83 Z" fill="#F0DDB8" stroke="#2B3A55" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<ellipse cx="100" cy="146" rx="19" ry="22" fill="#FBF0DC" stroke="#2B3A55" stroke-width="3"/>
<g class="lb-fur" fill="#FBF0DC" stroke="#2B3A55" stroke-width="2.5" stroke-linejoin="round">
<path d="M64 108 l-12 5 l12 5"/>
<path d="M136 108 l12 5 l-12 5"/>
<path d="M83 55 l6 -10 l6 9"/>
<path d="M111 54 l7 -9 l5 11"/>
</g>
<g class="lb-face">
<g class="lb-eye lb-eye-l">
<ellipse cx="83" cy="91" rx="11" ry="13" fill="#FFFDF7" stroke="#2B3A55" stroke-width="3"/>
<circle cx="84" cy="94" r="7" fill="#2B3A55" stroke="none"/>
<circle cx="81" cy="88" r="2.2" fill="#FFFDF7" stroke="none"/>
</g>
<g class="lb-eye lb-eye-r">
<ellipse cx="117" cy="91" rx="11" ry="13" fill="#FFFDF7" stroke="#2B3A55" stroke-width="3"/>
<circle cx="118" cy="94" r="7" fill="#2B3A55" stroke="none"/>
<circle cx="115" cy="88" r="2.2" fill="#FFFDF7" stroke="none"/>
</g>
<ellipse cx="72" cy="108" rx="7" ry="4.5" fill="#FF8FA3" stroke="none" opacity="0.72"/>
<ellipse cx="128" cy="108" rx="7" ry="4.5" fill="#FF8FA3" stroke="none" opacity="0.72"/>
<path d="M72 111 C82 132 119 132 129 111 C119 120 82 120 72 111 Z" fill="#2B3A55" stroke="#2B3A55" stroke-width="3" stroke-linejoin="round"/>
<g class="lb-teeth" fill="#FFFDF7" stroke="none">
<path d="M78 115 l4 8 l4 -8 Z"/>
<path d="M83 118 l4 8 l4 -8 Z"/>
<path d="M88 119 l4 8 l4 -8 Z"/>
<path d="M94 120 l4 8 l4 -8 Z"/>
<path d="M100 120 l4 8 l4 -8 Z"/>
<path d="M106 120 l4 8 l4 -8 Z"/>
<path d="M112 119 l4 8 l4 -8 Z"/>
<path d="M117 118 l4 8 l4 -8 Z"/>
<path d="M122 115 l4 8 l4 -8 Z"/>
</g>
<path d="M91 109 C95 112 105 112 109 109" fill="none" stroke="#FFFDF7" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
</g>
</g>
</svg>
tips & gotchas
Mistakes we actually made
I first put the pink inner ears in their own detail group OUTSIDE the ear groups — the ears wiggled and the pink stayed frozen mid-air. Anything that must move with a part has to live inside that part's group.
Anchor each ear's transform-origin at its BASE (where it meets the head), not its center — otherwise the wiggle looks like the ear is levitating instead of pivoting.
The nine teeth are just the same little triangle nudged along the mouth's curve — vary the y a touch (115, 118, 119, 120…) so the row follows the smile instead of sitting on a ruler line.
Keep the bounce tiny (-6px) and slow (2.8s). Labubu is mischievous, not caffeinated — big fast hops read as panic.
make it yours
Remix it
- New fur coloureasy
Recolour the fur fills — try a grey Labubu (#D8D8D8) or a mint one, keeping the navy outlines.
- Mood swingeasy
Swap the grin path for a small 'o' mouth and raise the pupils for a surprised blind-box face.
- Heart eyeseasy
Replace the navy pupils with tiny pink hearts (reuse the lb-heart1 path, scaled down).
- Head tiltmedium
Add a slow rotate() on a group around the head + ears, offset from the bounce, so it cocks its head while it bounces.
challenge extension
Same opposite-beat trick, more limbs: give the two arms their own groups and a shared wave animation with a half-cycle delay, so Labubu waves both paws alternately while everything else keeps bouncing.