Skip to content
CoDdleCoDoodle home
Pixel ArtIntermediateHTML + CSS + JS

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
~48

the steps

Build it, one change at a time

  1. Set up the pixel grid

    One empty div becomes a 12-column grid. image-rendering: pixelated is the property doing the real job here — it keeps every pixel crisp instead of the browser smoothing tiny squares into a blur.

    <div id="potion"></div>
    
    #potion {
      display: grid;
      grid-template-columns: repeat(12, 12px);
      grid-auto-rows: 12px;
      image-rendering: pixelated;
    }
    

    an empty grid, 12 pixels wide. Nothing to see — the sprite lives in the data, not here yet.

  2. 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.

    // 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.

  3. 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.

  4. 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 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"></div>
Open in Playground

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.