Skip to content
CoDdleCoDoodle home
Tiny ScenesBeginnerSVG + CSS

Moss Art Frame

A soft preserved-moss picture frame hanging from a tiny nail — all warm wood, rounded green blobs, and one little mushroom tucked into the calm.

← you, in 25 minutes.

what you'll build

Here's the plan

A preserved-moss wall-art sticker — a warm wooden frame hanging from a tiny nail, filled edge-to-edge with soft rounded moss blobs in three greens. The shapes stay simple on purpose: little blobby paths, chunky navy outlines, one cream-capped mushroom, and a sunshine flower dot that makes the whole thing feel quietly alive.

You’ll learn

  • Building a tiny scene in layers: paper, hanging hardware, frame, background moss, then foreground details
  • Using overlapping rounded SVG paths to suggest preserved moss without drawing every leaf
  • Keeping repeated organic shapes in their own groups so each blob can breathe on a staggered phase
  • Animating calm motion with transform-only sway, scale pulses, and sparkle twinkles inside prefers-reduced-motion
Skill level
Beginner
Time
~25 min
Tools
Just a browser
Code type
SVG + CSS
Lines
~90

the steps

Build it, one change at a time

  1. Hang the empty frame

    Start with warm paper, a little nail, two sparkle props, and the empty wooden frame. The string and frame share one ma-sway group now, so the final animation can rotate everything gently from the nail instead of from the frame's center.

    <rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/>
    <ellipse cx="100" cy="174" rx="54" ry="10" fill="#2B3A55" opacity="0.12" stroke="none"/>
    <path class="ma-spark ma-spark1" d="M30 56 L34 64 L30 72 L26 64 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
    <path class="ma-spark ma-spark2" d="M169 72 L172 78 L169 84 L166 78 Z" fill="#FF8FA3" stroke="none"/>
    <path class="ma-spark ma-spark3" d="M43 139 L46 145 L43 151 L40 145 Z" fill="#FF6B5E" stroke="none" opacity="0.85"/>
    <!-- the nail stays fixed; the string and frame will sway from this point later -->
    <circle cx="100" cy="25" r="5" fill="#FF6B5E" stroke="#2B3A55" stroke-width="3"/>
    <g class="ma-sway">
      <path d="M100 30 L71 55 M100 30 L129 55" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
      <g class="ma-frame" stroke="#2B3A55" stroke-linejoin="round">
        <rect x="39" y="50" width="122" height="118" rx="15" fill="#D9A96B" stroke-width="4"/>
        <!-- the pale opening makes the first frame feel satisfyingly empty -->
        <rect x="50" y="61" width="100" height="96" rx="11" fill="#FBF2E5" stroke-width="3"/>
        <rect x="45" y="56" width="110" height="106" rx="13" fill="none" stroke="#B9824A" stroke-width="3" opacity="0.75"/>
      </g>
    </g>
    

    an empty little frame, already hanging calmly from its nail.

  2. Fill the back with moss

    Add the first moss layer as four big rounded blobs. They sit inside the frame opening and overlap just enough that your eye reads one lush preserved-moss mat — no tiny leaf drawing required.

    <g class="ma-moss ma-sway" stroke="#2B3A55" stroke-width="2.8" stroke-linejoin="round">
      <!-- oversized, overlapping blobs fill the opening edge-to-edge -->
      <g class="ma-blob ma-blob1">
        <path d="M50 68 C57 58 80 59 88 69 C98 66 110 74 109 88 C108 103 94 111 76 108 C61 111 49 101 50 86 Z" fill="#A8C98A"/>
      </g>
      <g class="ma-blob ma-blob2">
        <path d="M93 63 C107 55 133 61 140 76 C151 80 153 97 143 106 C132 116 108 112 101 101 C88 100 82 72 93 63 Z" fill="#A8C98A"/>
      </g>
      <g class="ma-blob ma-blob3">
        <path d="M51 104 C63 96 86 101 94 114 C105 120 99 145 84 152 C66 160 49 149 50 132 Z" fill="#A8C98A"/>
      </g>
      <g class="ma-blob ma-blob4">
        <path d="M92 110 C103 98 128 101 143 113 C154 121 151 145 137 153 C121 161 95 153 91 136 Z" fill="#A8C98A"/>
      </g>
    </g>
    

    the frame is officially full of soft mossy blobs.

  3. Tuck in the tiny details

    Layer deeper and paler blobs on top, then tuck in one cream-capped mushroom and a sunshine flower dot. The wood-grain ticks go last so the frame gets its warm craft-store personality back after all that green.

    <g class="ma-details ma-sway">
      <g class="ma-foreground" stroke="#2B3A55" stroke-width="2.7" stroke-linejoin="round">
        <!-- foreground blobs add the other two greens and make the moss feel plush -->
        <g class="ma-blob ma-blob5">
          <path d="M66 78 C78 69 100 76 102 90 C104 105 89 116 73 111 C59 109 55 88 66 78 Z" fill="#5F8F5B"/>
        </g>
        <g class="ma-blob ma-blob6">
          <path d="M112 78 C126 70 148 80 149 96 C150 112 130 120 116 112 C105 106 101 87 112 78 Z" fill="#D4E8B8"/>
        </g>
        <g class="ma-blob ma-blob7">
          <path d="M73 119 C84 109 105 113 111 128 C116 143 99 155 82 151 C67 148 61 130 73 119 Z" fill="#D4E8B8"/>
        </g>
        <g class="ma-blob ma-blob8">
          <path d="M111 118 C124 108 144 115 147 132 C150 148 132 158 117 151 C104 146 100 127 111 118 Z" fill="#5F8F5B"/>
        </g>
        <g class="ma-blob ma-blob9">
          <path d="M91 91 C103 82 123 88 127 102 C132 118 115 130 99 124 C85 119 80 101 91 91 Z" fill="#A8C98A"/>
        </g>
      </g>
      <!-- one mushroom, one flower dot, then quick wood ticks on the frame -->
      <g class="ma-mushroom" stroke="#2B3A55" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
        <path d="M64 127 C60 119 67 112 77 116 C84 118 86 126 82 131 C76 129 70 128 64 127 Z" fill="#FFFDF7"/>
        <path d="M70 128 C72 132 72 137 69 141 C73 143 79 142 82 139 C79 135 78 131 79 128 Z" fill="#F3D9B6"/>
        <circle cx="70" cy="121" r="1.5" fill="#FF8FA3" stroke="none"/>
        <circle cx="78" cy="123" r="1.4" fill="#FFD84D" stroke="none"/>
      </g>
      <g class="ma-flower" fill="#FFD84D" stroke="#2B3A55" stroke-width="1.8" stroke-linejoin="round">
        <circle cx="130" cy="93" r="3.4"/>
        <circle cx="130" cy="87" r="2.5"/>
        <circle cx="136" cy="93" r="2.5"/>
        <circle cx="130" cy="99" r="2.5"/>
        <circle cx="124" cy="93" r="2.5"/>
        <circle cx="130" cy="93" r="1.7" fill="#FF6B5E" stroke="none"/>
      </g>
      <g class="ma-grain" fill="none" stroke="#8D5E35" stroke-width="2.2" stroke-linecap="round" opacity="0.72">
        <path d="M55 60 q10 -4 20 0"/>
        <path d="M122 59 q12 4 23 0"/>
        <path d="M49 87 q-4 13 0 27"/>
        <path d="M151 82 q5 16 0 31"/>
        <path d="M58 161 q15 -5 31 0"/>
        <path d="M111 161 q16 5 31 0"/>
      </g>
    </g>
    

    now it has preserved-moss depth, tiny forest finds, and a handmade frame.

  4. Make it breathe

    The frame, string, moss, and ticks share a tiny half-degree sway from the nail. Each blob also scales from its own center with staggered delays, so the moss feels alive without getting busy; sparkles twinkle on the side, and the whole thing pauses politely when reduced motion is preferred.

    @media (prefers-reduced-motion: no-preference) {
      .ma .ma-sway { transform-box: view-box; transform-origin: 100px 25px; animation: ma-sway 6.8s ease-in-out infinite; }
      .ma .ma-blob { transform-box: fill-box; transform-origin: center; animation: ma-breathe 5.8s ease-in-out infinite; }
      /* stagger the same pulse so the moss breathes in soft waves */
      .ma .ma-blob2 { animation-delay: -0.8s; }
      .ma .ma-blob3 { animation-delay: -1.6s; }
      .ma .ma-blob4 { animation-delay: -2.4s; }
      .ma .ma-blob5 { animation-delay: -3.1s; }
      .ma .ma-blob6 { animation-delay: -1.2s; }
      .ma .ma-blob7 { animation-delay: -2.8s; }
      .ma .ma-blob8 { animation-delay: -4s; }
      .ma .ma-blob9 { animation-delay: -2s; }
      .ma .ma-spark { transform-box: fill-box; transform-origin: center; }
      .ma .ma-spark1 { animation: ma-twinkle 3.2s ease-in-out infinite; }
      .ma .ma-spark2 { animation: ma-twinkle 3.2s ease-in-out 0.9s infinite; }
      .ma .ma-spark3 { animation: ma-twinkle 3.2s ease-in-out 1.8s infinite; }
      @keyframes ma-sway { 0%, 100% { transform: rotate(-0.5deg); } 50% { transform: rotate(0.5deg); } }
      @keyframes ma-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.025); } }
      @keyframes ma-twinkle { 0%, 100% { opacity: 0.38; transform: scale(0.82); } 45% { opacity: 1; transform: scale(1.14); } }
    }
    

    the calmest doodle on the wall — just a sway, a breath, and a twinkle.

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="ma" viewBox="0 0 200 200" width="200" height="200" role="img" aria-label="A preserved moss art frame gently swaying while moss blobs breathe and sparkles twinkle">
  <rect x="0" y="0" width="200" height="200" rx="18" fill="#FBF2E5" stroke="none"/>
  <ellipse cx="100" cy="174" rx="54" ry="10" fill="#2B3A55" opacity="0.12" stroke="none"/>
  <path class="ma-spark ma-spark1" d="M30 56 L34 64 L30 72 L26 64 Z" fill="#FFD84D" stroke="#2B3A55" stroke-width="2" stroke-linejoin="round"/>
  <path class="ma-spark ma-spark2" d="M169 72 L172 78 L169 84 L166 78 Z" fill="#FF8FA3" stroke="none"/>
  <path class="ma-spark ma-spark3" d="M43 139 L46 145 L43 151 L40 145 Z" fill="#FF6B5E" stroke="none" opacity="0.85"/>
  <circle cx="100" cy="25" r="5" fill="#FF6B5E" stroke="#2B3A55" stroke-width="3"/>
  <g class="ma-sway">
    <path d="M100 30 L71 55 M100 30 L129 55" fill="none" stroke="#2B3A55" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
    <g class="ma-frame" stroke="#2B3A55" stroke-linejoin="round">
      <rect x="39" y="50" width="122" height="118" rx="15" fill="#D9A96B" stroke-width="4"/>
      <rect x="50" y="61" width="100" height="96" rx="11" fill="#FBF2E5" stroke-width="3"/>
      <rect x="45" y="56" width="110" height="106" rx="13" fill="none" stroke="#B9824A" stroke-width="3" opacity="0.75"/>
    </g>
  </g>
  <g class="ma-moss ma-sway" stroke="#2B3A55" stroke-width="2.8" stroke-linejoin="round">
    <g class="ma-blob ma-blob1">
      <path d="M50 68 C57 58 80 59 88 69 C98 66 110 74 109 88 C108 103 94 111 76 108 C61 111 49 101 50 86 Z" fill="#A8C98A"/>
    </g>
    <g class="ma-blob ma-blob2">
      <path d="M93 63 C107 55 133 61 140 76 C151 80 153 97 143 106 C132 116 108 112 101 101 C88 100 82 72 93 63 Z" fill="#A8C98A"/>
    </g>
    <g class="ma-blob ma-blob3">
      <path d="M51 104 C63 96 86 101 94 114 C105 120 99 145 84 152 C66 160 49 149 50 132 Z" fill="#A8C98A"/>
    </g>
    <g class="ma-blob ma-blob4">
      <path d="M92 110 C103 98 128 101 143 113 C154 121 151 145 137 153 C121 161 95 153 91 136 Z" fill="#A8C98A"/>
    </g>
  </g>
  <g class="ma-details ma-sway">
    <g class="ma-foreground" stroke="#2B3A55" stroke-width="2.7" stroke-linejoin="round">
      <g class="ma-blob ma-blob5">
        <path d="M66 78 C78 69 100 76 102 90 C104 105 89 116 73 111 C59 109 55 88 66 78 Z" fill="#5F8F5B"/>
      </g>
      <g class="ma-blob ma-blob6">
        <path d="M112 78 C126 70 148 80 149 96 C150 112 130 120 116 112 C105 106 101 87 112 78 Z" fill="#D4E8B8"/>
      </g>
      <g class="ma-blob ma-blob7">
        <path d="M73 119 C84 109 105 113 111 128 C116 143 99 155 82 151 C67 148 61 130 73 119 Z" fill="#D4E8B8"/>
      </g>
      <g class="ma-blob ma-blob8">
        <path d="M111 118 C124 108 144 115 147 132 C150 148 132 158 117 151 C104 146 100 127 111 118 Z" fill="#5F8F5B"/>
      </g>
      <g class="ma-blob ma-blob9">
        <path d="M91 91 C103 82 123 88 127 102 C132 118 115 130 99 124 C85 119 80 101 91 91 Z" fill="#A8C98A"/>
      </g>
    </g>
    <g class="ma-mushroom" stroke="#2B3A55" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
      <path d="M64 127 C60 119 67 112 77 116 C84 118 86 126 82 131 C76 129 70 128 64 127 Z" fill="#FFFDF7"/>
      <path d="M70 128 C72 132 72 137 69 141 C73 143 79 142 82 139 C79 135 78 131 79 128 Z" fill="#F3D9B6"/>
      <circle cx="70" cy="121" r="1.5" fill="#FF8FA3" stroke="none"/>
      <circle cx="78" cy="123" r="1.4" fill="#FFD84D" stroke="none"/>
    </g>
    <g class="ma-flower" fill="#FFD84D" stroke="#2B3A55" stroke-width="1.8" stroke-linejoin="round">
      <circle cx="130" cy="93" r="3.4"/>
      <circle cx="130" cy="87" r="2.5"/>
      <circle cx="136" cy="93" r="2.5"/>
      <circle cx="130" cy="99" r="2.5"/>
      <circle cx="124" cy="93" r="2.5"/>
      <circle cx="130" cy="93" r="1.7" fill="#FF6B5E" stroke="none"/>
    </g>
    <g class="ma-grain" fill="none" stroke="#8D5E35" stroke-width="2.2" stroke-linecap="round" opacity="0.72">
      <path d="M55 60 q10 -4 20 0"/>
      <path d="M122 59 q12 4 23 0"/>
      <path d="M49 87 q-4 13 0 27"/>
      <path d="M151 82 q5 16 0 31"/>
      <path d="M58 161 q15 -5 31 0"/>
      <path d="M111 161 q16 5 31 0"/>
    </g>
  </g>
</svg>
Open in Playground

tips & gotchas

Mistakes we actually made

I first made the moss blobs too leaf-shaped, and they started looking fussy. Big rounded paths are enough — preserved moss reads as texture because the shapes overlap.

I keep the sway under one degree. Once the frame swings wider, it stops feeling calm and starts feeling like someone slammed a door.

I put transform-box: fill-box on the blobs so each one breathes from itself, not from the whole SVG. That tiny detail makes the pulse feel organic instead of stretchy.

I add the wood-grain ticks after the moss, even though they belong to the frame, because paint order is the easiest way to keep those little marks visible.

make it yours

Remix it

  • Autumn mosseasy

    Swap the pale green for muted sage and recolour the flower dot coral for a warmer fall frame.

  • Round frameeasy

    Change the frame rectangles into circles and move the moss blobs inward so it becomes a little moss porthole.

  • More forest findseasy

    Add two tiny cream mushrooms or a pink berry dot cluster, keeping them tucked between existing blobs.

  • Parallax swaymedium

    Give the foreground details a barely different sway delay from the back moss so the frame feels layered without getting wobbly.

challenge extension

Turn the moss mat into a living gallery wall: draw two smaller frames beside this one, reuse the same ma-breathe keyframes with new delays, and make all three frames sway from different nail points.