Turn a UI screenshot into clean, semantic HTML or JSX with Tailwind.
Drop a screenshot. Forge the markup. Streamed live as the model writes it.
Live demo · Get started · Report a bug · Roadmap
PixelForge is a screenshot-to-code tool built to feel like an engineering instrument. Drop a screenshot of any interface, a marketing hero, a dashboard, a Wikipedia article, a mobile app screen, and watch GPT-4o stream HTML and Tailwind back as the model writes it, with a live preview rendering the markup as each token arrives.
The product has three destinations: a marketing landing page at /, the forge workspace at /forge, and a getting-started guide at /docs.
Built with Next.js 16, the Vercel AI SDK, and GPT-4o vision via GitHub Models.
Try it live: pixel-forge-three-nu.vercel.app
- [2026/07] Forge and Steel redesign, three real routes (landing, forge, docs), a light paper-and-steel design system, and a scroll-reveal landing page
- [2026/07] Server-side upload hardening, magic-byte signature checks, a decode/re-encode pass with a pixel-count ceiling, metadata stripped, a processing timeout, and a per-client rate limit on
/api/generate(seelib/upload-validation.tsandlib/rate-limit.ts) - [2026/07] Content Security Policy added to the sandboxed preview document
- [2026/07] Playwright test suite covering routing, the forge happy path, refinement, history, responsive layout, reduced motion, and the upload security boundary
- [2026/06] Refinement loop, once a generation is Ready, describe a follow-up change ("make the header sticky") instead of starting over; the model streams back a full updated document, not a diff, and the previous version moves to history
- [2026/06] Syntax-highlighted Code view via Shiki, themed to the panel, applied once a generation is Ready so streaming stays plain text and fast
- [2026/06] History now persists across reloads, last 10 generations stored in localStorage with downscaled JPEG thumbnails that survive page refresh
- [2026/06] Live deployment on Vercel, pixel-forge-three-nu.vercel.app
- [2026/06] Generation history (last 10 in session), framework toggle (HTML or JSX), one-click download, device-width preview (desktop / tablet / mobile), keyboard shortcuts (paste, forge, copy, download, history)
- [2026/06] Paste-a-screenshot directly with ⌘V / Ctrl+V, real drag-and-drop, "Try with an example" for first-time visitors
- [2026/05] Fidelity-tuned system prompt, inline SVG icons, gradient placeholders, full color reproduction (not grayscale)
- [2026/05] Initial release: streaming generation, sandboxed live preview
The product does one thing.
- Live token streaming. Code streams in as the model writes it. First token typically lands within ~2 seconds.
- Live preview. A sandboxed iframe with
srcDocrenders the partial HTML using Tailwind's Play CDN. The preview updates as the model writes. - Color and icon fidelity. The system prompt demands real brand colors and inline SVG icons matched to the source screenshot.
- HTML or JSX output. Toggle between raw HTML and React-ready JSX. The server-side prompt swaps in a JSX rider that handles
className, self-closing void elements, and camelCased event handlers. - Refinement loop. Once a generation is Ready, describe a change, "make the header sticky", instead of re-forging from scratch. The model reworks the current code in place, optionally re-sending the original screenshot for fidelity, and streams the full replacement document into the same Code and Preview surface.
- Syntax-highlighted code. The Code view renders plain text while streaming and switches to full Shiki highlighting once a generation is Ready, themed to match the panel. Toggling HTML/JSX re-highlights with the correct grammar.
- Persistent history. The last 10 generations survive page refresh, stored in localStorage with downscaled JPEG thumbnails. Restore any of them to keep iterating without losing earlier work.
- Device-width preview. Constrain the preview iframe to 375px (mobile) or 768px (tablet) to verify that generated breakpoints actually reflow.
- Real input ergonomics. Drag-and-drop, click-to-upload, and paste-a-screenshot with ⌘V/Ctrl+V. All three work because that's how people actually use screenshots.
- Keyboard-first controls. ⌘Enter to forge, ⌘C to copy, ⌘S to download, ⌘/ for the shortcuts panel.
- Error recovery. A failed generation surfaces a toast and an inline error block with a Retry button, it re-runs the last action, whether that was an initial forge or a refinement. A cancelled generation is never shown as an error.
- Sandboxed by design.
sandbox="allow-scripts"with noallow-same-origin, generated content cannot reach back into the host app.
This project includes a generated knowledge graph mapping components, functions, utilities, and design decisions across the codebase, including the Forge and Steel redesign and the three-route architecture below.
Graph stats (as of last regeneration):
- 519 nodes (components, functions, configs, concepts, design notes)
- 714 edges (dependencies, references, semantic relationships)
- 43 communities (natural clusters of related functionality)
- Core abstractions:
cn()utility (23 connections), the project roadmap,app/forge/page.tsx(11 connections), the component and lib layers
The codebase is a layered architecture across three routes, one API route, and shared hooks/lib:
app/
(marketing)/page.tsx - landing page: hero, features, process, specimens
forge/page.tsx - the forge workspace; thin composition layer over hooks + components
docs/page.tsx - getting-started guide
api/generate/route.ts - AI streaming endpoint; initial and refinement modes, upload hardening, rate limit
hooks/
usePixelForge.ts - generation state machine; forge/refine/retry, AbortController, object URL lifecycle
useHistory.ts - localStorage persistence; SSR-safe, versioned key pixelforge:history:v1
useKeyboardShortcuts.ts - global paste + keydown listeners; ref-pattern for stable registration
useReveal.ts - IntersectionObserver scroll-reveal, reduced-motion aware
components/
layout/Nav.tsx, Footer.tsx - shared chrome for landing and docs
landing/ - Hero, Process, Features, Specimens, Reveal
UploadDropzone.tsx - drag-drop zone, file input, example loader
PreviewCanvas.tsx - sandboxed iframe (sandbox="allow-scripts" only)
Toolbar.tsx - output controls, framework toggle, device width, telemetry
CodePanel.tsx - code view with line numbers, Shiki highlighting, error + retry state
RefinementBar.tsx - follow-up instruction input that drives the refinement loop, with suggestion chips
HistoryDrawer.tsx - history dialog with downscaled JPEG thumbnails
ShortcutsDialog.tsx - keyboard shortcuts dialog
lib/
types.ts - shared type definitions (Status, Framework, HistoryEntry, etc.)
highlight.ts - lazy-loaded Shiki singleton for the Code view
preview.ts - createPreviewDoc() for iframe srcDoc content, now with a CSP
upload-validation.ts - server-side image validation, magic-byte check, decode/re-encode, dimension ceiling
rate-limit.ts - in-memory fixed-window rate limit for /api/generate
suggestions.ts - refinement suggestion pool, sampled without replacement
scroll.ts - header-offset-aware smooth scroll for in-page anchors
utils.ts - cn(), formatBytes(), createThumbnail()
tests/ - Playwright suite: routing, landing, forge, upload security, preview CSP, responsive
- Interactive 3D graph:
docs/architecture/graph.html, open locally to zoom, pan, and click nodes - Full report:
docs/architecture/GRAPH_REPORT.md, communities, cohesion metrics, refactoring suggestions, isolated nodes - Raw graph data:
docs/architecture/graph.json, structured data for programmatic use
The graph makes it possible to understand the architecture without reading every file. It identifies isolated components, weak cohesion areas, missing edges, and which nodes bridge communities, the ones that cost the most when changed.
Generated with graphify.
PixelForge breaks down screenshot-to-code into a streaming pipeline:
-
Upload stage. The image is validated client-side (type, size up to 10MB), then sent as multipart form data to the
/api/generateroute handler. -
Inference stage. The route handler base64-encodes the image and constructs a multimodal chat completion request to GPT-4o via GitHub Models. A fidelity-tuned system prompt demands semantic HTML with Tailwind utility classes, inline SVG icons, real brand colors, and gradient placeholders for images, no markdown fences, no preamble. A refinement request swaps in the existing code and a natural-language instruction in place of a fresh screenshot. A rider in the system prompt requires the model to return the full updated document, never a diff.
-
Streaming stage. The model's response is returned as a text stream using the Vercel AI SDK's
streamText→toTextStreamResponse(). The client reads theReadableStreamchunk-by-chunk and updates state on every token. AnAbortControllercancels in-flight work if the user re-submits or navigates away. -
Render stage. As code streams in, a sandboxed iframe with
srcDocre-renders the partial HTML using Tailwind's Play CDN. The user sees code and preview evolving live, with an Idle → Forging → Ready status indicator and live byte/line telemetry.
The upload is never written to disk and never served back to a browser, it is validated, decoded, re-encoded, base64-encoded, and sent to the model. /api/generate enforces, server-side, regardless of what the client already checked:
- A size ceiling (10MB) and empty-file rejection.
- A declared-type allowlist (PNG, JPEG, WEBP only).
- A magic-byte signature check, cross-referenced against the declared type, a renamed PDF, an SVG, or a mismatched extension is rejected before any decode.
- A decode pass with a pixel-count ceiling, guarding against decompression-bomb-style inputs.
- A re-encode through a bounded decoder that strips EXIF and other metadata and neutralizes any trailing polyglot bytes, with a processing timeout.
- A per-client, fixed-window rate limit on the endpoint.
Rejections return a short, non-leaky message; the server logs a reason code, not raw image bytes. The sandboxed preview iframe (sandbox="allow-scripts", no allow-same-origin) also carries a Content Security Policy scoped to what the Tailwind CDN preview needs. See lib/upload-validation.ts, lib/rate-limit.ts, and lib/preview.ts.
npx playwright install chromium # first run only
npm testThe suite (tests/) builds and starts a production server, then covers routing across all three destinations, the forge happy path and refinement loop, history persistence, responsive layout at 375/768/1280px, reduced motion, the preview CSP, and the upload security boundary (oversized, empty, mismatched-signature, PDF-as-PNG, dimension-bomb, malformed, and rate-limited requests). Tests run serially, the in-memory rate limiter and the live GPT-4o calls are shared server-side state.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) |
| Language | TypeScript (strict) |
| Styling | Tailwind CSS v4 (CSS-first config) |
| UI primitives | shadcn/ui (radix-nova preset) |
| AI integration | Vercel AI SDK |
| Model | GPT-4o via GitHub Models |
| Icons | Lucide React |
| Syntax highlighting | Shiki |
| Notifications | Sonner |
| Hosting | Vercel |
Just want to try it? Open the live demo, no setup required.
To run it yourself or contribute, follow the steps below.
- Node.js 20 or higher
- A GitHub account with access to GitHub Models
- A GitHub Personal Access Token with
Models: Read-onlypermission
git clone https://github.com/TheMEGALODON55681/PixelForge.git
cd PixelForge
npm installCreate a .env.local file in the project root:
GITHUB_MODELS_TOKEN=your_github_pat_here
npm run devOpen http://localhost:3000.
| Key | Action |
|---|---|
⌘V / Ctrl+V |
Paste a screenshot from the clipboard |
⌘Enter / Ctrl+Enter |
Forge / re-forge code |
⌘Enter / Ctrl+Enter |
Submit a refinement (when the Refine input is focused) |
⌘C / Ctrl+C |
Copy generated code |
⌘S / Ctrl+S |
Download generated code |
⌘H / Ctrl+H |
Toggle the history drawer |
⌘/ / Ctrl+/ |
Show the shortcuts panel |
Esc |
Close any open dialog |
A few choices worth flagging:
Why GitHub Models instead of OpenAI directly?
GitHub Models is a free, OpenAI-compatible inference endpoint that gives access to GPT-4o-tier models without billing setup. The Vercel AI SDK works with it via createOpenAI({ baseURL }). GitHub Models supports the Chat Completions API but not the newer Responses API, so the provider is called via .chat() explicitly.
Why streaming? Vision generation takes 20–30 seconds. Without streaming, the UI freezes and feels broken. With streaming, the first token arrives within ~2 seconds and the user can read the markup as it forms, perceived latency drops by an order of magnitude.
Why an iframe with Tailwind Play CDN for preview?
Generated HTML uses arbitrary Tailwind classes that can't be known at build time. Rendering inline would require runtime JIT in the parent app and risks style leakage. A sandboxed iframe with srcDoc solves both: it loads Tailwind via CDN for runtime compilation, and sandbox="allow-scripts" (without allow-same-origin) isolates generated content from the host app.
Why force the model to skip markdown code fences (and clean them anyway)?
GPT-4o ignores explicit "no code fences" instructions a meaningful fraction of the time. The system prompt asks; a stripCodeFences regex cleans up when the model doesn't listen. Defense in depth, the same principle as validating input on both client and server.
Why one light theme, and a dark code panel inside it? The rest of the app is paper and steel, see DESIGN.md for the full token system. The one deliberately dark surface is the code output panel, styled to look like a real editor rather than the page around it. That contrast carries the "instrument" feel now.
Why localStorage for history instead of a database? History is a local aid to iteration. localStorage keeps it fast, private, and zero-infrastructure. Thumbnails are downscaled to a max of 200px on the long edge and stored as JPEG data URLs so they survive serialization across reloads, object URLs, which die with the page, are not used.
Near term:
- Production deployment on Vercel, live
- Persist session history across reloads
- Persist framework/device-width preferences across reloads
- Syntax highlighting in the Code view (Shiki)
- Forge and Steel redesign, landing, forge, and docs routes on the new design system
- Server-side upload hardening and preview CSP
- Playwright test suite
Mid term:
- Refinement loop, "make the header sticky," "use a 3-column grid", re-runs the model with the previous code + a natural-language instruction
- Multi-framework output: HTML, React (JSX/TSX), Vue SFC
- Element-level inspection: redraw a region of the screenshot and regenerate just that fragment
- Automated accessibility audit of generated output
Long term:
- Optional accounts + shareable generation links
- Design-system-aware generation (emit code in the user's component library)
- Figma frame and live-URL ingestion
- Inspired by
screenshot-to-codeby Abi Raja, the project that established this category. PixelForge is a from-scratch reimplementation built to explore the architecture firsthand using a different stack (Next.js + Vercel AI SDK vs. FastAPI + WebSockets). - UI primitives from shadcn/ui (radix-nova preset)
- Icons from Lucide
- Streaming via Vercel AI SDK
- Inference via GitHub Models
MIT, see LICENSE for details.
Built by Aryan Sharma · 2026