Food Jutsu
Two tiny hands lock into a summoning sign and, poof, dinner appears mid-air. It is snack magic with warm paper, chunky ink, and a bowl of noodles that knows how to make an entrance.
← you, in 35 minutes.
what you'll build
Here's the plan
A tiny food-summoning doodle: two mitten hands press into a generic anime-style hand sign, a soft poof cloud bursts open, and a steaming bowl of noodles pops into existence above it. The loop is timed so the hands squeeze first, the bowl overshoots into place, the steam rises, and everything fades out before the spell starts again.
You’ll learn
- Layering a readable mini scene back-to-front: paper, speed lines, hands, cloud, bowl, steam
- Drawing pressed mitten-hands with mirrored paths so the gesture reads at 200px
- Using transform-origin points that match the action: hand squeeze at the palms, bowl pop at its center, cloud burst at the poof
- Hiding animation restarts by keeping appearing elements opacity: 0 at both ends of the loop
- Skill level
- Intermediate
- Time
- ~35 min
- Tools
- Just a browser
- Code type
- SVG + CSS
- Lines
- ~83
the steps
Build it, one change at a time
Set the summoning stage
Start with warm paper, a soft oval shadow where the hands will sit, and a few speed lines and sparkles around the empty air. The shadow stays outside the hand group so it can breathe with the spell later while the hands do their own squeeze.
<rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/> <!-- the hands will press into the spell right above this shadow --> <ellipse class="fj-shadow" cx="100" cy="178" rx="48" ry="9" fill="#2B3A55" opacity="0.15" stroke="none"/> <!-- speed lines point toward the future poof without drawing the food yet --> <g class="fj-speed" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" opacity="0.5"> <path class="fj-line fj-line1" d="M34 84 L54 94"/> <path class="fj-line fj-line2" d="M42 116 L62 113"/> <path class="fj-line fj-line3" d="M166 82 L146 94"/> <path class="fj-line fj-line4" d="M158 117 L138 113"/> </g> <!-- little sticker sparks: static now, twinkly later --> <path class="fj-spark fj-spark1" d="M30 48 L34 57 L30 66 L26 57 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/> <path class="fj-spark fj-spark2" d="M170 48 L173 55 L170 62 L167 55 Z" fill="#FF6B5E" stroke="none"/> <path class="fj-spark fj-spark3" d="M42 139 L45 146 L42 153 L39 146 Z" fill="#FF8FA3" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>an empty summoning circle, minus the circle — just speed lines and suspense.
Press the hands together
Build the gesture as two mirrored mitten paths meeting on the center line. The cuffs sit behind the hands, the palms share a vertical press seam, and each hand gets its own group so the final animation can nudge them inward without redrawing anything.
<g class="fj-hands" stroke="#2B3A55" stroke-linecap="round" stroke-linejoin="round"> <!-- cuffs go first so the skin-tone hands overlap their tops --> <g class="fj-cuffs" stroke-width="3.5"> <path d="M66 159 C77 154 90 157 95 167 L88 185 C78 184 67 179 60 170 Z" fill="#FF6B5E"/> <path d="M134 159 C123 154 110 157 105 167 L112 185 C122 184 133 179 140 170 Z" fill="#FFD84D"/> </g> <!-- mirrored mitten hands: chunky enough to read at 200px --> <g class="fj-hand fj-hand-l" stroke-width="4"> <path d="M100 128 C96 120 88 119 83 125 C78 131 80 139 85 145 C79 146 76 151 78 157 C81 166 93 165 99 156 C102 151 102 139 100 128 Z" fill="#F2C6A0"/> <path d="M86 144 C91 146 96 145 100 141" fill="none" stroke-width="2.5" opacity="0.55"/> </g> <g class="fj-hand fj-hand-r" stroke-width="4"> <path d="M100 128 C104 120 112 119 117 125 C122 131 120 139 115 145 C121 146 124 151 122 157 C119 166 107 165 101 156 C98 151 98 139 100 128 Z" fill="#F2C6A0"/> <path d="M114 144 C109 146 104 145 100 141" fill="none" stroke-width="2.5" opacity="0.55"/> </g> <!-- the center seam sells the pressed-palms pose --> <path class="fj-press-line" d="M100 126 C101 138 101 151 100 161" fill="none" stroke-width="3" opacity="0.75"/> </g>two mitten hands locked in: snack spell pending.
Summon the noodles
Now add the effect stack above the hands: poof first, bowl on top, chopsticks crossing the rim, steam curls, and a few small magic diamonds. The bowl is a coral half-shell under a cream rim, with one wiggly noodle line so it reads instantly as ramen-ish without needing tiny detail.
<g class="fj-summon"> <!-- poof cloud sits between the hands and the food --> <g class="fj-cloud" fill="#FFFDF7" stroke="#2B3A55" stroke-width="3" stroke-linejoin="round"> <path d="M61 109 C54 107 51 99 57 94 C60 88 68 88 72 93 C77 83 91 84 95 94 C101 88 112 91 113 100 C122 98 128 105 124 112 C120 121 104 121 95 117 C85 123 68 121 61 109 Z"/> </g> <g class="fj-bowl" stroke="#2B3A55" stroke-linecap="round" stroke-linejoin="round"> <!-- chopsticks are just two warm diagonal strokes behind the rim --> <path d="M117 54 L151 35" fill="none" stroke="#A86944" stroke-width="3"/> <path d="M123 60 L156 42" fill="none" stroke="#A86944" stroke-width="3"/> <ellipse cx="100" cy="75" rx="36" ry="13" fill="#FFFDF7" stroke-width="4"/> <path d="M65 75 C68 99 82 111 100 111 C118 111 132 99 135 75 Z" fill="#FF6B5E" stroke-width="4"/> <path d="M75 78 C82 68 96 86 105 75 C112 67 124 74 126 82" fill="none" stroke="#FFD84D" stroke-width="4"/> </g> <!-- steam curls get their own group so they can rise together --> <g class="fj-steam" fill="none" stroke="#2B3A55" stroke-width="2.5" stroke-linecap="round" opacity="0.5"> <path d="M83 58 C75 50 92 45 82 37"/> <path d="M101 55 C94 47 109 43 101 34"/> <path d="M119 58 C111 50 127 46 118 38"/> </g> </g>poof, bowl, chopsticks, steam — the food has entered the chat.
Loop the spell
The hands squeeze inward first, then the poof cloud scales out and fades, while the bowl pops from small to slightly too big before settling. The important trick: cloud, bowl, steam, and magic pops are all opacity: 0 at the start AND end of the same 3.2s loop, so the replay hides its own reset.
@media (prefers-reduced-motion: no-preference) { .fj .fj-shadow { transform-box: fill-box; transform-origin: center; animation: fj-shadow 3.2s ease-in-out infinite; } .fj .fj-hands { transform-origin: 100px 164px; animation: fj-squeeze 3.2s ease-in-out infinite; } /* each hand nudges toward the center line during the squeeze */ .fj .fj-hand-l { transform-origin: 100px 150px; animation: fj-hand-left 3.2s ease-in-out infinite; } .fj .fj-hand-r { transform-origin: 100px 150px; animation: fj-hand-right 3.2s ease-in-out infinite; } .fj .fj-cloud { transform-origin: 100px 108px; animation: fj-cloud-burst 3.2s ease-out infinite; } .fj .fj-bowl { transform-origin: 100px 83px; animation: fj-bowl-pop 3.2s cubic-bezier(0.2, 0.9, 0.2, 1) infinite; } .fj .fj-steam { animation: fj-steam-rise 3.2s ease-in-out infinite; } } @keyframes fj-squeeze { 0%, 100% { transform: scale(1); } 14%, 24% { transform: scale(1.04, 0.97); } 36% { transform: scale(0.99, 1.02); } } @keyframes fj-hand-left { 0%, 100% { transform: translateX(0) rotate(0deg); } 14%, 24% { transform: translateX(2px) rotate(3deg); } 36% { transform: translateX(0) rotate(-1deg); } } @keyframes fj-hand-right { 0%, 100% { transform: translateX(0) rotate(0deg); } 14%, 24% { transform: translateX(-2px) rotate(-3deg); } 36% { transform: translateX(0) rotate(1deg); } } @keyframes fj-cloud-burst { 0%, 10%, 100% { opacity: 0; transform: scale(0.55); } 18% { opacity: 1; transform: scale(0.9); } 42% { opacity: 0.72; transform: scale(1.24); } 78% { opacity: 0; transform: scale(1.5); } } @keyframes fj-bowl-pop { 0%, 17%, 100% { opacity: 0; transform: translateY(15px) scale(0.35); } 29% { opacity: 1; transform: translateY(-4px) scale(1.14); } 40%, 72% { opacity: 1; transform: translateY(0) scale(1); } 88% { opacity: 0; transform: translateY(4px) scale(0.92); } } @keyframes fj-steam-rise { 0%, 28%, 100% { opacity: 0; transform: translateY(8px); } 42% { opacity: 0.58; transform: translateY(0); } 72% { opacity: 0.38; transform: translateY(-9px); } 88% { opacity: 0; transform: translateY(-15px); } }squeeze, poof, noodles, steam, fade — dinner on an infinite loop.
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="fj" viewBox="0 0 200 200" width="200" height="200" role="img" aria-label="A looping food jutsu doodle where pressed hands summon a steaming noodle bowl from a poof cloud">
<rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/>
<ellipse class="fj-shadow" cx="100" cy="178" rx="48" ry="9" fill="#2B3A55" opacity="0.15" stroke="none"/>
<g class="fj-speed" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" opacity="0.5">
<path class="fj-line fj-line1" d="M34 84 L54 94"/>
<path class="fj-line fj-line2" d="M42 116 L62 113"/>
<path class="fj-line fj-line3" d="M166 82 L146 94"/>
<path class="fj-line fj-line4" d="M158 117 L138 113"/>
</g>
<path class="fj-spark fj-spark1" d="M30 48 L34 57 L30 66 L26 57 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
<path class="fj-spark fj-spark2" d="M170 48 L173 55 L170 62 L167 55 Z" fill="#FF6B5E" stroke="none"/>
<path class="fj-spark fj-spark3" d="M42 139 L45 146 L42 153 L39 146 Z" fill="#FF8FA3" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
<g class="fj-hands" stroke="#2B3A55" stroke-linecap="round" stroke-linejoin="round">
<g class="fj-cuffs" stroke-width="3.5">
<path d="M66 159 C77 154 90 157 95 167 L88 185 C78 184 67 179 60 170 Z" fill="#FF6B5E"/>
<path d="M134 159 C123 154 110 157 105 167 L112 185 C122 184 133 179 140 170 Z" fill="#FFD84D"/>
<path d="M73 163 C80 166 85 170 89 177" fill="none" opacity="0.45"/>
<path d="M127 163 C120 166 115 170 111 177" fill="none" opacity="0.45"/>
</g>
<g class="fj-hand fj-hand-l" stroke-width="4">
<path d="M100 128 C96 120 88 119 83 125 C78 131 80 139 85 145 C79 146 76 151 78 157 C81 166 93 165 99 156 C102 151 102 139 100 128 Z" fill="#F2C6A0"/>
<path d="M86 144 C91 146 96 145 100 141" fill="none" stroke-width="2.5" opacity="0.55"/>
<path d="M84 132 C88 127 94 127 99 133" fill="none" stroke-width="2.5" opacity="0.5"/>
</g>
<g class="fj-hand fj-hand-r" stroke-width="4">
<path d="M100 128 C104 120 112 119 117 125 C122 131 120 139 115 145 C121 146 124 151 122 157 C119 166 107 165 101 156 C98 151 98 139 100 128 Z" fill="#F2C6A0"/>
<path d="M114 144 C109 146 104 145 100 141" fill="none" stroke-width="2.5" opacity="0.55"/>
<path d="M116 132 C112 127 106 127 101 133" fill="none" stroke-width="2.5" opacity="0.5"/>
</g>
<path class="fj-press-line" d="M100 126 C101 138 101 151 100 161" fill="none" stroke-width="3" opacity="0.75"/>
</g>
<g class="fj-summon">
<g class="fj-cloud" fill="#FFFDF7" stroke="#2B3A55" stroke-width="3" stroke-linejoin="round">
<path d="M61 109 C54 107 51 99 57 94 C60 88 68 88 72 93 C77 83 91 84 95 94 C101 88 112 91 113 100 C122 98 128 105 124 112 C120 121 104 121 95 117 C85 123 68 121 61 109 Z"/>
<circle cx="71" cy="101" r="10" fill="#FF8FA3" opacity="0.42" stroke="none"/>
<circle cx="108" cy="104" r="8" fill="#FFD84D" opacity="0.34" stroke="none"/>
</g>
<g class="fj-bowl" stroke="#2B3A55" stroke-linecap="round" stroke-linejoin="round">
<path d="M117 54 L151 35" fill="none" stroke="#A86944" stroke-width="3"/>
<path d="M123 60 L156 42" fill="none" stroke="#A86944" stroke-width="3"/>
<ellipse cx="100" cy="75" rx="36" ry="13" fill="#FFFDF7" stroke-width="4"/>
<path d="M65 75 C68 99 82 111 100 111 C118 111 132 99 135 75 Z" fill="#FF6B5E" stroke-width="4"/>
<path d="M75 78 C82 68 96 86 105 75 C112 67 124 74 126 82" fill="none" stroke="#FFD84D" stroke-width="4"/>
<path d="M83 94 C94 101 108 101 119 94" fill="none" stroke="#FFFDF7" stroke-width="3" opacity="0.7"/>
<path d="M88 106 C95 109 106 109 113 106" fill="none" stroke="#2B3A55" stroke-width="2.5" opacity="0.35"/>
</g>
<g class="fj-steam" fill="none" stroke="#2B3A55" stroke-width="2.5" stroke-linecap="round" opacity="0.5">
<path d="M83 58 C75 50 92 45 82 37"/>
<path d="M101 55 C94 47 109 43 101 34"/>
<path d="M119 58 C111 50 127 46 118 38"/>
</g>
<g class="fj-magic" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round">
<path class="fj-pop fj-pop1" d="M50 78 L53 85 L50 92 L47 85 Z"/>
<path class="fj-pop fj-pop2" d="M151 72 L154 79 L151 86 L148 79 Z" fill="#FF8FA3"/>
<path class="fj-pop fj-pop3" d="M137 123 L140 129 L137 135 L134 129 Z" fill="#FFFDF7"/>
</g>
</g>
</svg>
tips & gotchas
Mistakes we actually made
My first hands were too realistic and turned into tiny beige puzzles. The mitten version reads faster — big outer blobs, one center seam, and just enough wrinkle lines to say 'fingers live here'.
The bowl needs to appear around y=55-95, not down in the cloud. When I placed it lower, the poof swallowed the silhouette and the whole gag became soup fog.
transform-origin is the difference between magic and mess: the hands squeeze from the palms, the bowl pops from its own middle, and the cloud bursts from the poof's center.
I fade the summoned pieces out before 100%, even though it feels early. That little quiet gap makes the loop restart cleanly instead of snapping from 'full dinner' back to 'no dinner'.
make it yours
Remix it
- Rice ball summoneasy
Replace the bowl group with a rounded triangle onigiri, a navy seaweed rectangle, and one tiny sparkle at the top.
- Extra spicyeasy
Add three small coral flame shapes behind the bowl and animate them with the same fj-pop-twinkle timing.
- Double servingmedium
Duplicate fj-bowl, scale the copy to 0.72, shift it left, and delay its pop by 0.18s for a second snack landing.
- Recipe roulettemedium
Create two alternate food groups and cycle their opacity in different thirds of the same 3.2s loop.
challenge extension
Make the spell feel even snappier: add a thin circular shockwave behind the cloud that scales from 0.3 to 1.8 and fades out between 16% and 45%, while the bowl still stays invisible at both ends of the loop.