Paper Plane
A crisp little paper plane shoots up-right with chunky navy folds, a loopy dashed trail, and one tiny coral heart stamp on the wing. It is the kind of SVG icon that looks like it knows exactly where recess is happening.
← you, in 25 minutes.
what you'll build
Here's the plan
A folded paper-plane icon flying up-right on warm paper, with simple cream and sky-tint facets doing the heavy reading work. Behind it, a dashed loop-de-loop trail marches smoothly, while small clouds and sparkle props keep the whole doodle playful without turning it into a full scene.
You’ll learn
- Building an SVG icon from a stage layer, a bold silhouette, and one detail pass
- Using simple polygon-like paths and flat fills to make paper folds read quickly
- Drawing a dashed flight trail that loops cleanly by matching dash math to the animation
- Animating with transform, opacity, and stroke-dashoffset inside reduced-motion support
- Skill level
- Beginner
- Time
- ~25 min
- Tools
- Just a browser
- Code type
- SVG + CSS
- Lines
- ~105
the steps
Build it, one change at a time
Set the sky sticker
Start with the warm paper square, then add two tiny cloud puffs and a few sparkle props around the edges. There is no ground shadow here, because this icon should feel airborne from the first layer.
<rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/> <g class="pp-cloud pp-cloud1" fill="#FFFDF7" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity="0.82"> <path d="M23 128 C23 122 28 118 34 119 C37 112 48 112 52 120 C59 119 64 124 64 130 C64 136 59 139 52 139 H33 C27 139 23 135 23 128 Z"/> </g> <g class="pp-cloud pp-cloud2" fill="#FFFDF7" stroke="#2B3A55" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" opacity="0.7"> <path d="M139 43 C139 38 143 35 148 36 C151 30 160 31 163 38 C169 38 173 42 173 47 C173 52 169 55 163 55 H149 C143 55 139 51 139 43 Z"/> </g> <path class="pp-spark pp-spark1" d="M34 57 L38 65 L34 73 L30 65 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/> <path class="pp-spark pp-spark2" d="M164 132 L168 139 L164 146 L160 139 Z" fill="#FF8FA3" stroke="none"/> <circle class="pp-spark pp-dot1" cx="53" cy="37" r="3" fill="#6FA8FF" stroke="none"/> <circle class="pp-spark pp-dot2" cx="173" cy="77" r="2.5" fill="#FFD84D" stroke="none"/>paper sky, tiny clouds, flight vibes warming up.
Fold the plane silhouette
Block the paper plane as one big sharp shape, then drop in two flat facets before the outline goes on top. The geometry stays crisp on purpose, because icons need to read faster than little scene doodles.
<g class="pp-plane" stroke-linecap="round" stroke-linejoin="round"> <path class="pp-plane-fill" d="M48 122 L162 52 L129 151 L101 113 Z" fill="#FFFDF7" stroke="none"/> <path class="pp-wing-facet" d="M48 122 L101 113 L129 151 L91 132 Z" fill="#EFE7D8" stroke="none"/> <path class="pp-nose-facet" d="M101 113 L162 52 L116 124 Z" fill="#DCE9FF" stroke="none"/> <path class="pp-plane-outline" d="M48 122 L162 52 L129 151 L101 113 Z" fill="none" stroke="#2B3A55" stroke-width="4"/> </g>one clean paper dart, already headed up-right.
Draw the loop-de-loop
Now add the dashed trail behind the plane, then draw the fold lines, heart stamp, and one extra sparkle. The trail uses an explicit dash pattern now so the CSS can march it later without a visible jump.
<path class="pp-trail" d="M24 151 C43 135 42 116 60 113 C78 110 81 135 62 138 C40 142 37 96 76 92 C98 90 107 105 92 119 C80 130 67 126 55 121" fill="none" stroke="#6FA8FF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 8"/> <path class="pp-fold pp-fold-main" d="M48 122 L101 113 L162 52" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> <path class="pp-fold pp-fold-wing" d="M101 113 L129 151" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round"/> <path class="pp-fold pp-fold-tip" d="M101 113 L116 124" fill="none" stroke="#2B3A55" stroke-width="2.4" stroke-linecap="round" opacity="0.72"/> <path class="pp-stamp" d="M85 124 C82 120 75 122 76 128 C77 134 86 138 89 141 C92 137 99 131 98 125 C97 120 90 120 88 124 Z" fill="#FF6B5E" stroke="#2B3A55" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path class="pp-spark pp-spark3" d="M142 111 L146 119 L142 127 L138 119 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>a confident little swoop, with bonus heart mail.
Make it glide
The plane banks with a tiny rotate-and-lift loop, while the trail moves by changing stroke-dashoffset from zero to a clean multiple of its dash period. Sparkles scale and fade, clouds drift by a pixel or two, and the whole motion layer stays inside reduced-motion support.
@media (prefers-reduced-motion: no-preference) { .pp .pp-plane { transform-box: fill-box; transform-origin: center; animation: pp-plane-bank 3.4s ease-in-out infinite; } .pp .pp-trail { animation: pp-trail-march 1.4s linear infinite; } .pp .pp-spark { transform-box: fill-box; transform-origin: center; } .pp .pp-spark1 { animation: pp-twinkle 2.8s ease-in-out infinite; } .pp .pp-spark2 { animation: pp-twinkle 2.8s ease-in-out 0.55s infinite; } .pp .pp-spark3 { animation: pp-twinkle 2.8s ease-in-out 1.1s infinite; } .pp .pp-dot1 { animation: pp-twinkle 3.2s ease-in-out 1.45s infinite; } .pp .pp-dot2 { animation: pp-twinkle 3.2s ease-in-out 1.9s infinite; } .pp .pp-cloud { transform-box: fill-box; transform-origin: center; } .pp .pp-cloud1 { animation: pp-cloud-drift-a 6.8s ease-in-out infinite; } .pp .pp-cloud2 { animation: pp-cloud-drift-b 7.4s ease-in-out 0.8s infinite; } } @keyframes pp-plane-bank { 0%, 100% { transform: rotate(-2deg) translateY(0); } 50% { transform: rotate(3deg) translateY(-2px); } } @keyframes pp-trail-march { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: -28; } } @keyframes pp-twinkle { 0%, 100% { opacity: 0.34; transform: scale(0.84); } 45% { opacity: 1; transform: scale(1.14); } } @keyframes pp-cloud-drift-a { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(2px); } } @keyframes pp-cloud-drift-b { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-1.5px); } }banking gently while the trail does the tidy dash math.
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="pp" viewBox="0 0 200 200" width="200" height="200" role="img" aria-label="A folded paper plane icon gently banking while its looped dashed flight trail marches behind it">
<rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/>
<g class="pp-cloud pp-cloud1" fill="#FFFDF7" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity="0.82">
<path d="M23 128 C23 122 28 118 34 119 C37 112 48 112 52 120 C59 119 64 124 64 130 C64 136 59 139 52 139 H33 C27 139 23 135 23 128 Z"/>
</g>
<g class="pp-cloud pp-cloud2" fill="#FFFDF7" stroke="#2B3A55" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" opacity="0.7">
<path d="M139 43 C139 38 143 35 148 36 C151 30 160 31 163 38 C169 38 173 42 173 47 C173 52 169 55 163 55 H149 C143 55 139 51 139 43 Z"/>
</g>
<path class="pp-spark pp-spark1" d="M34 57 L38 65 L34 73 L30 65 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
<path class="pp-spark pp-spark2" d="M164 132 L168 139 L164 146 L160 139 Z" fill="#FF8FA3" stroke="none"/>
<circle class="pp-spark pp-dot1" cx="53" cy="37" r="3" fill="#6FA8FF" stroke="none"/>
<circle class="pp-spark pp-dot2" cx="173" cy="77" r="2.5" fill="#FFD84D" stroke="none"/>
<path class="pp-trail" d="M24 151 C43 135 42 116 60 113 C78 110 81 135 62 138 C40 142 37 96 76 92 C98 90 107 105 92 119 C80 130 67 126 55 121" fill="none" stroke="#6FA8FF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="6 8"/>
<g class="pp-plane" stroke-linecap="round" stroke-linejoin="round">
<path class="pp-plane-fill" d="M48 122 L162 52 L129 151 L101 113 Z" fill="#FFFDF7" stroke="none"/>
<path class="pp-wing-facet" d="M48 122 L101 113 L129 151 L91 132 Z" fill="#EFE7D8" stroke="none"/>
<path class="pp-nose-facet" d="M101 113 L162 52 L116 124 Z" fill="#DCE9FF" stroke="none"/>
<path class="pp-plane-outline" d="M48 122 L162 52 L129 151 L101 113 Z" fill="none" stroke="#2B3A55" stroke-width="4"/>
<path class="pp-fold pp-fold-main" d="M48 122 L101 113 L162 52" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<path class="pp-fold pp-fold-wing" d="M101 113 L129 151" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round"/>
<path class="pp-fold pp-fold-tip" d="M101 113 L116 124" fill="none" stroke="#2B3A55" stroke-width="2.4" stroke-linecap="round" opacity="0.72"/>
<path class="pp-stamp" d="M85 124 C82 120 75 122 76 128 C77 134 86 138 89 141 C92 137 99 131 98 125 C97 120 90 120 88 124 Z" fill="#FF6B5E" stroke="#2B3A55" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path class="pp-spark pp-spark3" d="M142 111 L146 119 L142 127 L138 119 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
</svg>
tips & gotchas
Mistakes we actually made
I keep the plane made from just a few large facets, because tiny fold clutter makes an icon feel nervous.
I place the trail before the plane in the final SVG so the tail connection feels tucked behind the paper.
I match the dash animation to the dash period before choosing the timing; that keeps the loop restart invisible.
I use clouds as tiny props, not scenery, because the plane should stay the first read.
make it yours
Remix it
- Change the stampeasy
Swap the coral heart for a star, dot, or tiny initial on the lower wing.
- Make it night maileasy
Turn the sky tint darker, change the clouds to pale moons, and keep the cream plane bright.
- Double the trailmedium
Add a second thinner dashed path with a delayed dashoffset animation for a breezier flight path.
- Fold a fleetmedium
Duplicate the plane at smaller sizes and stagger their banking animations like a tiny paper-air show.
challenge extension
Turn this icon into a mini sticker set: create two more simple SVG icons using the same warm paper, navy outline, and one accent color rule, then make each one animate with a different transform-only loop.