Pixel Potion
Pixel art drawn from a string map — edit the letters, change the sprite.
← you, in 35 minutes.
what you'll build
Here's the plan
A potion bottle built from a single string array — each letter in the map becomes one colored pixel. Edit a letter, redraw the sprite. HTML, CSS, and a few lines of JavaScript.
You’ll learn
- Drawing pixel art from a data array instead of hand-placing every pixel
- Using CSS grid (grid-template-columns + grid-auto-rows) as a pixel canvas
- image-rendering: pixelated to keep small sprites crisp instead of blurry
- Layering color in passes — outline first, fills second — the way a real sketch works
- Skill level
- Intermediate
- Time
- ~35 min
- Tools
- Just a browser
- Code type
- HTML + CSS + JS
- Lines
- ~88
the steps
Build it, one change at a time
Set up the pixel scene grid
One scene div holds the shelf, brick pixels, sparkles, and an empty 12-column potion grid. Every piece is aligned to the same 12px unit, and image-rendering: pixelated keeps the tiny squares crisp instead of blurry.
<div id="potion-scene" aria-label="Pixel potion on a dungeon shelf"> <span class="brick brick-1"></span> <span class="brick brick-2"></span> <span class="brick brick-3"></span> <span class="sparkle sparkle-1"></span> <span class="sparkle sparkle-2"></span> <span class="sparkle sparkle-3"></span> <span class="shelf"></span> <div id="potion"></div> </div> #potion-scene { position: relative; width: 192px; height: 192px; background: #FBF2E5; image-rendering: pixelated; } #potion-scene .shelf, #potion-scene .brick, #potion-scene .sparkle { position: absolute; display: block; } #potion-scene .shelf { left: 12px; top: 156px; width: 168px; height: 12px; background: #2B3A55; } #potion-scene .brick { width: 36px; height: 12px; background: #E8DCCB; } #potion-scene .brick-1 { left: 0; top: 12px; } #potion-scene .brick-2 { left: 24px; top: 24px; width: 24px; } #potion-scene .brick-3 { right: 0; top: 132px; } #potion-scene .sparkle { width: 12px; height: 12px; background: #FFD84D; box-shadow: 12px 0 #FFFDF7, -12px 0 #FFFDF7, 0 12px #FFD84D, 0 -12px #FFD84D; } #potion-scene .sparkle-1 { left: 156px; top: 36px; } #potion-scene .sparkle-2 { left: 24px; top: 84px; } #potion-scene .sparkle-3 { left: 144px; top: 108px; } #potion { position: absolute; left: 24px; top: 12px; display: grid; grid-template-columns: repeat(12, 12px); grid-auto-rows: 12px; image-rendering: pixelated; }a paper dungeon backdrop: shelf pixels, brick pixels, sparkles, and an empty potion grid waiting for data.
Draw the sprite from a map
The sprite is a string array — one character per pixel, a dot for empty space. A short loop reads every row and character, and creates one <span> per non-dot pixel with a class matching its letter. Only the outline color (k) is styled so far, so just the ink sketch shows through on the shelf scene.
// the sprite lives in this map — edit letters, redraw the potion const MAP = [ "....cccc....", "....cccc....", "....kkkk....", "....kggk....", "...kkggkk...", "..kggggggk..", ".kgghgggggk.", ".kgllllllgk.", ".kllllllllk.", ".kllllllllk.", "..kllllllk..", "...kkkkkk...", ]; const el = document.getElementById("potion"); for (const row of MAP) { for (const ch of row) { const px = document.createElement("span"); if (ch !== ".") px.className = ch; el.appendChild(px); } }just the ink outline — the sprite's sketch layer, drawn straight from the letters in MAP and sitting on the shelf.
Fill in the colors
Every remaining letter in the map already has a matching CSS class waiting for it — cork, glass, shine, liquid. Adding their colors is the whole step; the JS loop from the last step never changes.
#potion span.c { background: #FFA94D; } /* cork */ #potion span.g { background: #E6EFFF; } /* glass */ #potion span.h { background: #FFFDF7; } /* shine */ #potion span.l { background: #FF6B5E; } /* liquid */full color. A little potion, pixel by pixel, tucked into its dungeon-shelf scene.
Make the liquid glow
One keyframe animating filter: brightness on the liquid pixels is enough to sell a magical glow — no gradients, no glow filters, no extra elements.
#potion span.l { background: #FF6B5E; animation: glow 2s ease-in-out infinite; } @keyframes glow { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.25); } }the finished dungeon-shelf potion, glowing like it means it.
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.
<div id="potion-scene" aria-label="Pixel potion on a dungeon shelf">
<span class="brick brick-1"></span>
<span class="brick brick-2"></span>
<span class="brick brick-3"></span>
<span class="sparkle sparkle-1"></span>
<span class="sparkle sparkle-2"></span>
<span class="sparkle sparkle-3"></span>
<span class="shelf"></span>
<div id="potion"></div>
</div>
tips & gotchas
Mistakes we actually made
If the grid renders as one long row instead of a square, check grid-auto-rows is set — grid-template-columns alone only defines the columns.
The loop skips "." characters on purpose — every dot is empty space, not a pixel.
Class names are single letters (c, k, g, h, l) purely to keep MAP readable as a little picture in the source — nothing stops you from renaming them.
make it yours
Remix it
- New label coloreasy
Change span.c's background for a different cork.
- Bigger spriteeasy
Bump 12px to 20px in both the grid and the pixel sizing for a chunkier sprite.
- Recolor by handmedium
Replace a few "l" letters with "g" in MAP to draw a half-empty bottle.
- New spritemedium
Design your own MAP from scratch: a heart, a mushroom, a tiny sword.
challenge extension
Same map-to-pixels technique, new object: draw a pixel heart that pulses instead of glows — same animation trick, different keyframe values.
keep sketching
More tutorials to try
Pick another small recipe and borrow one technique for your next doodle.
- Avatar: Seven HavensSVG + CSS · fan-art + fantasy · animation tutorial
- BulbasaurSVG + CSS · paths + fan-art · animation tutorial
- Butter YellowSVG + CSS · doodle + fashion · animation tutorial
- ClayfaceSVG + CSS · fan-art + clay · animation tutorial
- CrybabySVG + CSS · fan-art + crybaby · animation tutorial