Turn any photo into a playful hand-drawn doodle avatar β powered by AI.
π Live Demo β’ β¨ Skills β’ π Quick Start β’ π€ Contributing
Doodle AI is a conversational creative studio that turns your photos into hand-drawn doodle art. Chat with an AI agent, attach a photo, pick a skill β and get a unique doodle avatar, collage, sticker sheet, or gift image in seconds.
There are two ways in:
- The chat studio β describe what you want, attach a photo, and the agent picks the right skill. Full run of 23 skills.
- Free-tool pages β single-purpose landing pages (e.g.
/photo-to-coloring-page/,/doodle-ideas/) where the real prompt composer is the first thing on the page and the matching skill is already pre-pinned. 10 are live; the hub is at doodleai.art/tools. Same generator, same threads, just entered from a specific query instead of a blank chat.
Try it now: doodleai.art β 5 free credits on signup, no credit card needed.
Every generation style is a pluggable Skill β a self-contained SKILL.md under src/mastra/skills/. There are 23 runnable skills today, from single-image avatars to multi-image packs (Sticker Pack, Expression Pack, Seasonal Pack). A few, with real output:
![]() Doodle Avatar 1:1 hand-drawn avatar from a photo |
![]() Sticker Pack Die-cut sticker sheet image |
![]() Pet Portrait Your pet as a hand-drawn character |
![]() Surprise Me Fictional character, no photo needed |
The full, always-current catalogue β including the newer Coloring Page (coloring, printable line art) and Doodle Idea (idea, draw a typed idea with no photo) β is data-driven from the skill files and rendered at doodleai.art/skills. This README deliberately does not list all 23: the count above is interpolated from the same source the app reads, and the picker page can't drift because it is the source.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser (Astro 5 SSG + client scripts) β
β Chat β Prompt Composer β Skill Picker β Canvas β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β POST /api/chat
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ
β Cloudflare Worker (doodleai-agent) β
β Mastra Agent β picks skill β calls generate tool β
β Credits: 1/gen, 5 free on signup, refund on fail β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββ
β PicX API
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββ
β PicX Studio β image generation + CDN hosting β
β Permanent URL returned immediately β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Layer | Tech |
|---|---|
| Frontend | Astro 5, TypeScript, scoped CSS |
| Runtime | Cloudflare Workers + D1 + KV |
| Agent | Mastra framework |
| Model | OpenRouter (configurable, default: Gemini 3.7 Flash) |
| Generation | PicX API (server-owned key) |
| Auth | Better Auth (Google OAuth) |
| Canvas | tldraw 5.3 (React island) |
| Package manager | pnpm |
git clone https://github.com/Type-Think-AI/doodle-ai.git
cd doodle-ai
pnpm install
cp .dev.vars.example .dev.vars
# Fill in: OPENROUTER_API_KEY, PICX_API_KEY, GOOGLE_CLIENT_ID/SECRET, BETTER_AUTH_SECRET
pnpm dev:localOpen http://localhost:4321 β local D1/KV, no Cloudflare account needed.
| Variable | Required | Description |
|---|---|---|
OPENROUTER_API_KEY |
β | Routes to the configured LLM |
OPENROUTER_MODEL |
β | Default: google/gemini-3.7-flash |
PICX_API_KEY |
β | Server-owned key for image generation |
BETTER_AUTH_SECRET |
β | Session signing secret |
GOOGLE_CLIENT_ID |
β | Google OAuth |
GOOGLE_CLIENT_SECRET |
β | Google OAuth |
Every skill is a self-contained directory under src/mastra/skills/:
src/mastra/skills/your-skill/
βββ SKILL.md # Agent-selection description + a `metadata:` block
βββ references/ # Optional supporting docs
A runnable skill (one the agent can generate with) has to be wired into four places. This isn't ceremony β three of the four are enforced by tsc or the loader precisely because the interesting failure mode is silent: a skill that half-exists, generates images, and never gets charged for them.
-
SKILL.mdβ top-leveldescription(this is what the agent reads to pick the skill), plus ametadata:block withid,displayName,runnable: true,requiresPhoto,aspectRatio, etc. Theidundermetadata:β not the directory name β is the key everything else references. -
Register a prompt builder in
src/lib/prompts/index.ts. Single-image skills add an entry toSKILL_PROMPT_BUILDERS; multi-image "pack" skills add one toSKILL_PACK_BUILDERS(and their id toPACK_SKILL_IDSincosts.ts). The builder function itself lives one-per-file undersrc/lib/prompts/. Without a registered builder the skill falls through to the generic single-image prompt at run time. -
Add the id to
GENERATION_MODESinsrc/lib/doodle-constants.ts. This is the canonical list;src/lib/skill-loader.tsasserts at build that the set ofrunnable: trueids andGENERATION_MODESmatch exactly in both directions β a runnable skill missing from the list, or a mode with no skill, fails the build. -
Add an entry to
IMAGES_PER_RUNinsrc/lib/credits/costs.ts. This map is typedRecord<GenerationMode, number>, so the moment you add an id toGENERATION_MODES(step 3) without pricing it here,tscfails with a missing-property error. This is the one that matters most: credits are resolved server-side from the skill id alone, so an unpriced skill would generate images while charging nothing. Making it a typed record means "ships a skill that generates for free" is a compile error, not a production incident. Pack skills also cross-check: an id inPACK_SKILL_IDSpriced at 1 image (or a >1 entry not in the list) throws at module load.
Then run pnpm build β the loader validates the whole chain and fails with a message pointing at whatever is missing.
src/
βββ mastra/ # Agent, skills (SKILL.md), and generation tool
βββ pages/ # Astro routes (/, /c/[id], /skills/, /tools/, /settings, /api/*)
βββ components/ # UI: Sidebar, Composer, Canvas, Footer, Navbar
βββ scripts/app/ # Client controllers (chat, sidebar, media-picker)
βββ lib/ # Auth, credits, content, admin queries
βββ content/ # Editorial articles (keyword-first URLs)
βββ db/ # Drizzle schema (D1)
βββ styles/ # Global CSS layers
We welcome contributions! See CONTRIBUTING.md for guidelines.
Good first issues:
- Add a new skill (a
SKILL.md+ a registered prompt builder β see Adding a New Skill) - Improve an existing article in
src/content/articles/ - Fix a bug from the issues tab
# Validate your changes
pnpm exec tsc --noEmit
pnpm build
pnpm lint- 23 generation skills β single-image (avatar, collage, pet, couple, chibi, crayon, faceless, gift, coloring, idea, family, occupation, β¦) and multi-image packs (stickers, mood-captions, expressions, seasonal, style-roll, childhood, festival, webtoon)
- Free-tool landing pages with the composer above the fold +
/tools/hub (10 live) - tldraw infinite canvas for generated images
- Multiplayer roadmap board (Durable Objects)
- Team workspaces and shared credits (org-owned ledger, invites, per-member spend, credit transfer)
- SEO: keyword-first URLs, JSON-LD, FAQ schema, llms.txt
- Stripe checkout for credit packs (schema in place; checkout not switched on yet)
- User-created custom skills
- Video generation (provider-neutral adapter)
- WhatsApp + Discord channel connectors
- Character continuity across generations
MIT Β© 2026 Type-Think-AI







