AI Game Asset Generation: A Working Pipeline (Sprites to UI)

AI Game Asset Generation: A Working Pipeline (Sprites to UI)

Honesty note: this is the exact pipeline I run for my game (Merge Fish 2048) — AI generates the art, then a small Python post-processing script turns it into game-ready assets. No proprietary tricks, just reproducible steps with code you can adapt.

TL;DR

  1. AI art needs a post-processing step before it’s game-ready. Generated sprites arrive with white backgrounds, mixed sizes, and no text on buttons — every one of those is fixable with a small Python script.
  2. Never let the AI “remove the background” on white-background art. It cuts holes in white fish bellies and white button edges. A pixel-threshold flood approach (below) keeps the art intact.
  3. Standardize sizes in the pipeline, not in the engine. Resize once to your target dimensions (buttons 320×96, board 720×720, bubbles 48/72/96) so the game never does layout math at runtime.
  4. Build the pipeline as a re-runnable script, not a one-off. When the AI generates a v2 of your title art, you re-run one file and get consistent output — that’s what makes AI iteration cheap.

The pipeline at a glance

AI generates art (white background, any size)

1. download_assets()  →  local copies (re-runnable, no re-download)

2. remove_white_bg()  →  transparent background, edge-gradient preserved

3. trim_to_content()  →  crop away transparent margins

4. resize / canvas-pad  →  exact target sizes (buttons, board, bubbles)

5. add_button_text()  →  centered text with shadow + outline

6. crop particles from clusters  →  small/medium/large bubbles

Each step is deterministic — run the script again, get the same output. That’s the property that makes AI iteration cheap.

Step 1 — Download with a re-runnable pattern

ASSETS = {
    "title": "https://aka.doubaocdn.com/s/QOeyPeHUkB",
    "btn_green": "https://aka.doubaocdn.com/s/wUv6VJ79ll",
    "btn_blue": "https://aka.doubaocdn.com/s/aBSZJnk7N4",
    "board_bg": "https://aka.doubaocdn.com/s/Gu5aY9yVuK",
    "bubble_single": "https://aka.doubaocdn.com/s/zZAVmnj0ug",
    "bubble_cluster": "https://aka.doubaocdn.com/s/0bs0qpC6cu",
}

def download_assets():
    for name, url in ASSETS.items():
        path = os.path.join(TMP_DIR, name + ".png")
        if not os.path.exists(path):
            urllib.request.urlretrieve(url, path)

Key idea: skip downloads that already exist. Re-running the script doesn’t re-fetch anything, so you can regenerate downstream steps freely.

Step 2 — Remove white backgrounds without destroying art

This is the step that matters most. A naive “make white transparent” turns fish bellies and button highlights into holes. The fix is a two-threshold gradient:

def remove_white_bg(img, threshold=235, edge_threshold=200):
    img = img.convert("RGBA")
    pixels = img.load()
    w, h = img.size
    for y in range(h):
        for x in range(w):
            r, g, b, a = pixels[x, y]
            min_val = min(r, g, b)
            if min_val >= threshold:
                pixels[x, y] = (r, g, b, 0)          # clearly white → transparent
            elif min_val >= edge_threshold:
                # near-white → partially transparent, proportional to whiteness
                alpha = int((threshold - min_val) / (threshold - edge_threshold) * 255)
                pixels[x, y] = (r, g, b, alpha)
    return img

Why this works: pure white (min_val ≥ 235) goes fully transparent; near-white edge pixels (200–235) keep a soft alpha gradient, so anti-aliased edges and subtle highlights survive. A single hard cut at 235 would fringe every curve.

Tune per asset: title art is forgiving (240/210), particle bubbles need gentler edges (240/215). Keep the thresholds as parameters.

Step 3 — Trim to content

def trim_to_content(img):
    bbox = img.getbbox()
    if bbox:
        return img.crop(bbox)
    return img

AI generations have unpredictable margins. Cropping to the alpha bounding box removes that variance before resizing, so your target-size math is exact.

Step 4 — Standardize sizes

Resize or canvas-pad to exact targets so the game engine never scales at runtime:

target_w, target_h = 320, 96
img = img.resize((target_w, target_h), Image.LANCZOS)

# canvas-pad to a square (keeps aspect, centers content):
canvas = Image.new("RGBA", (target, target), (0,0,0,0))
canvas.paste(img, ((target-new_w)//2, (target-new_h)//2), img)

My actual targets: buttons 320×96 and 380×96, board background 720×720, single bubble 256×256, particles 48/72/96.

Step 5 — Add text to buttons

Buttons generated by AI rarely include the text you need. Draw it on with a shadow and a rounded outline so it reads on any background:

draw.text((tx+2, ty+3), text, font=font, fill=(0,0,0,100))   # shadow
for dx in range(-outline_width, outline_width+1):            # outline
    for dy in range(-outline_width, outline_width+1):
        if dx*dx + dy*dy <= outline_width*outline_width:
            draw.text((tx+dx, ty+dy), text, font=font, fill=outline_color)
draw.text((tx, ty), text, font=font, fill=text_color)        # main

Use a bold system font (Impact or Arial Bold) — game UI text needs weight at small sizes. The outline is drawn as a filled circle sweep, which keeps it round instead of square.

Step 6 — Crop particles from a cluster

Instead of asking the AI for three separate bubble sizes, generate one cluster and crop:

small = img.crop((int(w*0.05), int(h*0.1), int(w*0.25), int(h*0.3)))
medium = img.crop((int(w*0.3), int(h*0.3), int(w*0.55), int(h*0.55)))
large = img.crop((int(w*0.6), int(h*0.25), int(w*0.9), int(h*0.55)))

Then trim + resize each to 48/72/96. This gives you a consistent particle family from one source image.

Common pitfalls

  1. Single-threshold background removal → holes and fringes in light-colored art. Always use the gradient version.
  2. Not trimming before resize → aspect-ratio math is off, content gets scaled into wrong margins.
  3. Letting the engine scale at runtime → blurry sprites and layout drift across screen sizes. Bake the target size into the asset.
  4. Hand-editing generated files → the next AI iteration overwrites your fixes. Put every transformation in the script, so re-runs reproduce everything.
  5. Ignoring file size → 2048×2048 AI exports bloat your build. Resize down to what the game actually shows (see the size guide).

Bottom line

AI generates art fast; a deterministic post-processing script makes it consistent. The two-threshold background removal is the single highest-value step — it’s the difference between professional-looking sprites and art that looks AI-made. Once the pipeline exists, regenerating assets after an AI iteration takes minutes, not hours. This is one piece of the full AI-assisted workflow that turned my solo timeline into two prototypes.