Skip to content
Dashboard

Pixel Portraits: AI generated trading cards

Daniel LinthwaiteBrand Designer

Link to headingBringing Pixel Portraits into v0

Link to headingHow it works:

// app/actions.ts
"use server"
import * as fal from "@fal-ai/serverless-client"
// Configure fal.ai with your API key
fal.config({
credentials: process.env.FAL_KEY,
})
// The prompt that transforms photos into pixel art
const PIXEL_ART_PROMPT = `Image roles
Image 1 (user photo): sole source of identity, facial geometry, proportions, hairline, hairstyle, silhouette, and recognisable likeness.
Image 2 (reference): pixel grid, colour discipline, framing, scale, and composition schema only.
Step 1: Identity Projection
Project the user photo onto a coarse 24×24 pixel grid, preserving the user's exact facial geometry: head width, jaw shape, cheek structure, eye spacing, nose width, mouth position, hairline, and overall silhouette.
Each major facial region (forehead, cheeks, nose, jaw, hair mass) must resolve into a small number of large pixel blocks, not fine detail.
This step defines identity and geometry only. No stylistic averaging.
Step 2: Pixel Formatting
Format the projected identity using ultra-blocky 16-bit pixel art rules, matching the reference image's pixel size, framing, scale, and negative space, without altering identity geometry.
Treat the portrait as if it were hand-placed pixel art, not a filtered image.
Pixel Art Rules
- Use a maximum of 10 colours total
- Render as if created at 24×24 pixels, then upscale with no smoothing
- All pixels must be large, crisp, and perfectly square
- Pixel adjacency must be hard, stepped, and blocky, with no blending, interpolation, or feathering
- Do not subdivide pixel clusters into smaller internal details
- No dithering, no gradients, no soft shading
Shading must be posterised and minimal, using 1 highlight and 1 shadow tone per facial region at most.
Apply lighting with a right-side light source, so the left side of the face is darker, using large, clearly separated pixel blocks to express contrast.
Facial Construction
Construct hair, beard, and facial features as large geometric pixel clusters derived directly from the user photo's real silhouette and contours.
Hair must read as a single or very small number of chunky shapes, not many interlocking fragments.
Cluster scale may match the reference, but cluster shapes must follow the user's geometry, not the reference's.
Facial features must remain recognisable, but expressed with the fewest pixels possible:
eye spacing, eyebrow angle, nose width, mouth shape, jawline, and hairline must map clearly to the user photo without added detail.
Use classic retro-game proportions: simplified forms, bold contours, minimal detail, slightly enlarged eyes relative to resolution.
Rendering Constraints
- No photorealistic texture
- No strands, pores, wrinkles, micro-shading, or smooth curves
- No secondary highlights or contouring
- Use thick pixel outlines around the head, facial features, and clothing silhouette
- Character faces forward
- Figure is centered on a flat green background
Clothing
Clothing must match the type and silhouette from the user photo but be rendered entirely in black, using at most two dark tones for minimal pixel-art shading.'
// Load the style reference image as base64
async function getStyleReferenceBase64(): Promise<string> {
const baseUrl = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "http://localhost:3000"
const response = await fetch(`${baseUrl}/style_reference.png`)
const blob = await response.blob()
const buffer = await blob.arrayBuffer()
const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)))
return `data:image/png;base64,${base64}`
}
export async function generatePixelPortrait(userImageDataUrl: string) {
// Load the style reference image
const styleReference = await getStyleReferenceBase64()
// Call fal.ai's GPT Image 1.5 Edit model
// Pass user image first, style reference second
const result = await fal.subscribe("fal-ai/gpt-image-1.5/edit", {
input: {
prompt: PIXEL_ART_PROMPT,
image_urls: [userImageDataUrl, styleReference],
image_size: "auto",
quality: "high",
input_fidelity: "high",
num_images: 1,
output_format: "png",
},
})
return result.images[0].url
}

Link to headingVercelf Yourself: a festive remix

Apply the same 16 bit pixel art portrait style shown in the reference image to the user photo. Match the scale, crop and framing of the character in the style reference so the portrait aligns visually with the reference image. The final generated image must be strictly greyscale and use only black, grey and white. Do not introduce any colour. The character should face forward and wear a fluffy black fur coat inspired by Wham!’s Last Christmas video. Add two black and white retro 1980s skis in the foreground on the left side of the portrait, standing vertically as if the character is holding them. Add a silver hoop earring on the character’s right ear. Update the hairstyle to an 80s-inspired version of the user’s hair while keeping it recognisable. Preserve recognisable attributes from the user photo including skintone and facial features so the pixel character clearly resembles the person. Place the figure centered on a black background with a dark grey snowflake pattern.

route.ts
const result = await generateText({
model: "google/gemini-3-pro-image",
messages: [{
role: "user",
content: [
{ type: "image", image: userImage },
{ type: "image", image: styleReferenceImage },
{ type: "text", text: prompt },
],
}],
})

Link to headingWrapping up

Ready to deploy?