Skip to content

Repository files navigation

PawCSS — Your Dog, Rebuilt with CSS

Upload a dog photo. PawCSS samples it pixel by pixel in the browser and reconstructs it as thousands of real HTML elements styled with CSS — then lets you compare, zoom, inspect and export the result.

Why it was built

Hackathon brief: build something that is visually delightful and technically honest. Most "photo → art" toys are a CSS filter over an <img>. PawCSS does the opposite: the original image never appears in the artwork. What you see is generated DOM.

Why this isn't just a CSS filter

  • The uploaded photo is drawn into an offscreen <canvas> at a tiny resolution (40², 70² or 100²).
  • getImageData() reads back every pixel; each pixel becomes an artwork cell with its own hex color, luminance, ASCII glyph and scale.
  • Each cell is rendered as its own DOM node (<i> or <span>) inside a CSS Grid — the artwork contains 0 <img> elements, and the stats panel reports that number from the actual render.
  • The export writes standalone HTML/CSS that still renders your dog with the photo deleted.

How an uploaded image becomes CSS

File → validate → decode → downscale (≤900px, JPEG q0.86, for the Before panel)
     → canvas Nx N draw → getImageData → per-cell {rgb, luma}
     → optional median-cut quantization (Pixel/Neon modes)
     → Artwork model {cols, rows, cells[]}
     → React renders one DOM element per cell (CSS Grid)
     → exporter maps unique colors to palette classes → standalone HTML+CSS

Art modes

Mode Technique
CSS Mosaic (showcase) One div per sampled pixel, full color fidelity
Pixel Pup Larger cells + 24-color median-cut palette
Neon Pup 16-color palette, saturation boost, per-cell box-shadow glow on a dark stage
Halftone Pup Circular cells (border-radius: 50%) scaled by sampled brightness
Terminal Pup ASCII ramp glyphs colored per cell on a terminal background

Features

  • Drag-and-drop / file-picker upload, fully client-side (nothing is uploaded for the art pipeline)
  • Draggable Before/After comparison slider (mouse, touch, and a keyboard-accessible range input)
  • Zoom control (1× → 6×) with a live overlay of CSS pixels / colors / rendered elements
  • Inspect My Dog: a devtools-style panel; click any cell to see its generated rule (.dog-pixel-482 { … }) and copy it
  • Real statistics — element counts and unique-color counts are computed from the generated artwork, never hardcoded
  • Export: Download HTML + CSS (standalone file) and Copy CSS
  • Resolution presets Low 40² / Medium 70² / High 100² to keep the DOM sane on mobile

Architecture

src/lib/imageProcessing.ts   validation, decode, compression, sampling, median-cut quantization
src/lib/artwork.ts           Artwork model + per-cell CSS rule generation
src/lib/exportArtwork.ts     palette-class HTML/CSS exporter + download helper
src/lib/demoArtwork.ts       hand-authored hero sample (still real DOM cells)
src/lib/dogProfile.functions.ts  server function calling Gemini through the AI gateway

src/components/pawcss/
  LandingHero, ImageUploader, ArtStyleSelector, CSSArtwork, FittedArtwork,
  ComparisonSlider, ArtworkStats, CSSInspector, DogPersonality,
  ExportPanel, GenerationProgress
src/routes/index.tsx         app shell / orchestration

Image logic is pure TypeScript with no React imports, so it is testable and reusable.

Google AI integration ("Meet Your Dog")

After upload, the downscaled photo is sent to a server function which calls Gemini (vision) through the Lovable AI gateway and returns a playful profile: invented name, likely breed mix, energy 1–5, cuddle level 1–5, vibe, one-line description and a "dog thought". The key lives only in LOVABLE_API_KEY on the server — it is never exposed to the browser. Rate limits, missing keys, malformed JSON and network failures all fall back to friendly placeholder content, and the CSS art pipeline never depends on it. Breed output is clearly labelled a playful estimate, not a veterinary or genetic identification.

Optional "Let My Dog Speak"

Speaks a line built from the AI personality. It currently uses the browser's built-in SpeechSynthesis API so it works with zero keys and zero cost; swapping in ElevenLabs only requires a server function that returns audio for the same sentence. The button is hidden when speech synthesis is unavailable and can never break the core app.

Run locally

bun install
bun run dev     # http://localhost:8080

Environment variables

Variable Required Purpose
LOVABLE_API_KEY optional Server-side Gemini access for "Meet Your Dog". Without it the app shows fallback profile content.

Exporting

Download HTML + CSS produces a self-contained document: a .pawcss-art grid, one CSS class per unique color, and one element per cell. Because colors are deduplicated into palette classes, a 10,000-cell artwork stays a reasonable file size while remaining pixel-identical to what you see on screen. Copy CSS copies just the stylesheet.

Third-party libraries

TanStack Start/Router/Query (framework), Tailwind CSS v4 + shadcn/ui primitives (Radix) for the design system, lucide-react (icons), sonner (toasts), zod (server-function input validation). All image processing, quantization, artwork generation and export code is original and written for this hackathon.

Technical challenges & solutions

  • DOM weight — 100² = 10,000 nodes. Solved with resolution presets, a lightweight <i> element per cell, and a scale transform (FittedArtwork) instead of re-rendering at different sizes.
  • Export size — per-cell rules would be megabytes; palette classes cut this dramatically.
  • Faithful color — naive nearest-neighbour sampling looked muddy; high-quality canvas smoothing plus alpha compositing onto the cream background fixed it.
  • Honest stats — every number is derived from the artwork model at render time.

Future improvements

Dithering for the pixel mode, SVG/PNG export, shareable permalinks, CSS-variable-driven animated artwork, worker-based sampling for very large grids, and true ElevenLabs voices per personality.

About

Turn Your Dog Into CSS Art.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages