mir-ashiq/canvix
mir-ashiq/canvix has 181 GitHub stars and 49 forks. GitHot measured +3 stars in 24 hours. Last checked 2026-11-09 18:08 UTC. Last push 2026-11-03.
Canvix — Free & open-source graphic design tool. A community-built Canva alternative with a drag-and-drop editor, templates, shapes, text, images & one-click export. Works on desktop, tablet & mobile.
What you can do
- Editor, templates, uploads, draw, groups, rulers, crop - ✅ always local
- Exports — PNG / JPG / PDF / SVG / video - ✅ always local
- Collaboration, presence, comments - ✅ only needs the database
- Background remover - ✅ local (in-browser ONNX)
Measured star growth
- in 24h
- +3
- in 7 days
- Update delayed
- in 15 days
- Update delayed
- in 1 month
- Update delayed
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
README.md
OriginalCanvix
Design anything. Free for everyone.
Free & open-source graphic design tool — a community-built Canva alternative.
Features · Quick start · Self-hosting · Roadmap · Contributing
✨ What is Canvix?
Canvix is a browser-based design tool that lets anyone create social media posts, presentations, posters, logos and thumbnails — with a drag-and-drop editor, a 110-template library and one-click export. It runs entirely on your own infrastructure, stores designs in your own database, and exports files locally in your browser. Free accounts keep your designs yours; anonymous link-editing keeps working too. No watermarks, no paywalls.
🆓 Free forever — the promise
Every feature in Canvix is free, and it stays free. Forever. There is no Pro tier, no subscription, no trial countdown, no watermark ransom, no "premium" element you can drag but not drop. The whole point of this project is a design tool that never asks you for a credit card:
- All 110 templates — including the entire premium collection — are free
- All exports (PNG, JPG, SVG, PDF, video) are free and unwatermarked
- All AI features (magic edit, magic write, background removal, translate) run against your own API keys, so there is nothing for us to meter
- Accounts are free — username + password, no email, no verification, no external provider; a self-hoster runs the whole thing for $0
- Self-hosting is first-class: your server, your database, your rules
- The license is MIT — you can even fork it and build a business on it
If anyone ever asks you to pay for Canvix, you are being scammed: the source is right here.
🎨 Features
v0.9.0 — Accounts & per-design ownership
- Free accounts, zero dependencies — username + password with scrypt
hashing from
node:cryptoand opaque DB-backed sessions (HttpOnly, SameSite=Lax cookies; the DB stores only sha256(token) so a leak never yields usable sessions). No email, no OAuth, no external provider — deliberately self-contained so self-hosting stays free forever. - Per-design ownership — designs you create while signed in are private to your account: the dashboard scopes to “yours + shared”, and every mutating API (save, delete, restore, version snapshot) answers 403 for anyone else. Share links still open — in view-only mode with an honest banner in the editor.
- Nothing breaks for existing setups — ownerless designs keep the exact
pre-v0.9 link-editable behaviour. One click (“Save my designs to this
account”) migrates the designs this browser created into your new
account — anonymous creations are attributed to the creating browser
(
canvix_anoncookie, sha256-hashed in the DB), so adopt-all can never sweep up another person's work. Legacy designs from before attribution exist? Claim those one-by-one with the per-design “Save to my account” action (a deliberate, visible action for pre-accounts self-hosters). - CSRF-safe writes — every mutating API route enforces same-origin on
top of the SameSite=Lax session cookie; cross-site POST/PUT/DELETE
requests answer
403 CSRF_REJECTED. Non-browser clients (curl, the MCP endpoint) are unaffected. - Collab identity upgrade — signed-in users appear under their real username in presence, cursors and comments.
- DNS-rebinding pinning (SSRF close-out) — the MCP server's
safeFetchnow connects via a custom lookup pinned to the validated IP, closing the TOCTOU window documented in v0.6.1. TLS keeps the real hostname for SNI, so certificate validation is unaffected. - End-to-end CI verification — the “Canvix Database Setup” workflow now boots the built server against your real database and exercises the whole account + ownership + claim-safety + CSRF flow (register → me → owned create → owner save → anonymous 403 → logout → revoked-token replay → login → cross-origin rejection → two-browser adopt-all scoping → cross-account isolation) before cleaning up after itself.
v0.8.0 — Supabase-ready infrastructure & the 110-template library
- Supabase-first database support — switching hosts (Neon → Supabase,
or anything else) is now a two-command move:
- Real migrations —
prisma/migrationsships in the repo, sonpm run db:setupcreates the full schema on a brand-new database (Supabase, Neon, RDS, local…) and seeds the template library in one go - Self-healing setup — if your database was provisioned with the
pre-v0.8
db pushflow,db:setupdetects it, baselines the migration history (Prisma's documented path) and continues instead of dead-ending - Smart connection handling (
src/lib/db-url.ts) — a copy-pasted Supabase connection string just works: TLS is auto-required for remote hosts, and the transaction pooler (port 6543) automatically gets pgbouncer-compatible settings so prepared statements don't break /api/health— a one-page diagnosis of env → connectivity → migration → seed, with the exact fix printed at each step- Actionable errors everywhere — “Could not open template” is dead:
API failures now carry classified codes (
DB_NOT_MIGRATED,DB_UNREACHABLE…), the dashboard shows a setup banner with the fix, and toasts tell you exactly what to run
- Real migrations —
- The template library, ×1.8 — now 110 templates, all free — 50 new
2026-trend originals across every category, authored in-house (podcast
quote card, new-listing post, bootcamp, restaurant special, lookbook,
webinar, LinkedIn banner, recipe pin, birthday story, Black Friday,
playlist cover, flash-sale countdown, grand opening · investor pitch,
marketing plan, course, agency portfolio, quarterly review, demo day,
nonprofit impact, product launch · wedding invitation, gym flyer, cafe
menu, concert poster, yoga flyer, gift certificate, business card, open
mic, moving sale · executive & creative resumes, cover letter, proposal,
invoice, newsletter, whitepaper, agenda, press release · 7 thumbnails ·
3 logos). The library lives in
src/lib/templates/as collections so it can keep growing, and a validator (scripts/validate-templates.ts) checks every entry at CI time
v0.7.0 — Fast & Free Forever: rendering performance & the premium template collection
- Rendering performance — the canvas was rebuilt around three classic
hot-path killers, and every one is gone:
- Structural sharing in the editor store — every mutation now builds only the pages/elements it actually touches; untouched objects keep their identity. A keystroke, a slider drag or a remote/AI live edit re-renders exactly one canvas node instead of the whole scene, and deep-cloning the entire document per edit is history
- O(1) undo/redo — history snapshots now share structure with live state (like Immer, without Immer): pushing history is a reference copy, and a 60-step history of a large design costs a fraction of the old memory
- Memoized canvas nodes —
ElementNodeno longer subscribes to the whole store (it reads actions at event time viagetState()), is wrapped inReact.memo, and receives stable callbacks — so selection changes, panel toggles and remote cursors don't re-render a single shape - Split Konva layers — interaction chrome (transformer, snap guides,
marquee, ruler guides, draw preview) moved to its own
<canvas>layer: dragging an element no longer repaints the chrome, and guide updates no longer repaint the artwork - Snap-guide equality guard — identical guide sets don't trigger a React render; snapping during drag only redraws when something visibly changed
- Shared image cache — images decode once per URL and are reused across pages, thumbnails and duplicated nodes (with in-flight dedup and a bounded 300-entry LRU)
perfectDrawEnabled: falseon element nodes — skips Konva's hit-canvas perfection pass during drag/transform for a big interactive win- Lazy template thumbnails — preview stages mount via IntersectionObserver (300 px pre-roll), so scrolling the 110-template library stays smooth; only visible cards own a canvas
- Locked in by tests:
src/store/editor-store.test.tsasserts the identity guarantees (untouched pages/elements keep references, history shares structure, snapshots are never mutated in place)
- The premium template collection — all free — 39 new templates (21 → 60), every one designed to Canva-grade quality and every one unlocked: social (flash sale story, trattoria, open house, bootcamp, webinar, podcast cover, birthday story, wanderlust, LinkedIn banner, minimal quote), presentations (pitch deck noir, quarterly report, webinar deck, course welcome, roadmap, meet-the-team), print (neon gig poster, charity gala, artisan menu, spa voucher, yard sign, luxe card, wedding thank-you, coupon), documents (proposal, newsletter, ebook cover, whitepaper, press release, meeting agenda, studio invoice, case study), thumbnails (gaming, tutorial, podcast episode, unboxing) and logos (orbit, bloom, apex). The templates panel gets a "Premium collection · free forever" section — premium is a quality label here, never a paywall
v0.6.0 — the Agent Edition: MCP, tables, frames, curved text & embeds
- MCP server (Model Context Protocol) — Canvix becomes an agent-native design platform:
POST /api/mcpspeaks standard MCP (JSON-RPC 2.0 over Streamable HTTP, stateless — same transport profile Canva's AI Connector uses), so AI assistants like Claude Desktop, Cursor, ChatGPT and any MCP client can create and edit real designs on your server. 20 tools: browse templates, search/get/create designs, add text/shapes/images/tables/embeds, restyle & move elements, set backgrounds, animate pages, comment, and export (SVG/PNG/JSON) — plusgenerate_design, which asks the server AI provider for a structured, fully-validated page layout and materializes it as native editable elements. Every tool maps to a real subsystem; without a provider everything works exceptgenerate_design, which says so plainly. Agent edits append to the same event log the editor replays, so a design an agent is editing updates live for humans in the editor - Security by default — the endpoint stays disabled until you set
CANVIX_MCP_TOKEN; requests needAuthorization: Bearer <token>(timing-safe compare), cross-origin requests are rejected, per-IP rate limits apply, image fetches are SSRF-guarded (private hosts denied, 8 MB cap), patches are whitelisted & clamped per element type, and errors never leak stack traces - Tables — Canva-style native tables: click any cell on the canvas to edit it, add/remove rows & columns from the toolbar, style borders/header/text, four starter styles. Cells export to SVG and translate with the design
- Frames — image-in-shape frames (rectangle, rounded, ellipse, circle,
triangle, hexagon). Drop an upload or photo on a frame to fill it — the image is
clipped to the shape with cover-fit (also true vector
<clipPath>in SVG export) - Curved text — Canva's signature arc effect: curve any text −180°…+180°
(arch up / valley down), rendered as a real text-on-path (Konva
TextPath, SVG<textPath>) so it stays crisp in PNG/PDF/video exports and editable everywhere - Embed cards — YouTube videos, Google Maps places and any link become native vector link cards (media band, play/pin glyph, title). Clicking a card in Preview or shared views opens the URL — honest scope: a styled card, not an iframe
- Translate covers tables — the AI translator now also walks table cells, not just text boxes
- Dashboard “AI agents (MCP)” card — shows the live endpoint status and a copy-paste client config (endpoint URL + bearer headers) for Claude Desktop / Cursor / any MCP client
v0.5.0 — Magic Layers, real-time collaboration & production AI infrastructure
- Magic Layers — upload any flat design (or pick an image on your canvas) and a vision model decomposes it into real, editable Canvix layers: text becomes live text boxes (color, weight and size recovered), photos become cropped image elements, shapes become native shapes, the background becomes a page background. Every region carries a confidence score — uncertain regions are flagged amber in the review step and can be discarded. Honest limitations are surfaced in-product (fonts are matched by class, not recovered exactly; illustrations become image regions)
- Real-time collaboration — share a design link and edit together: live
multiplayer with colored cursors + name tags, remote selection outlines,
presence avatars in the top bar, a live/offline connection indicator, and
gap-free catch-up after disconnects. Architecture: an append-only event log in
Postgres + server-sent events downstream + batched op POSTs upstream — works on
Vercel serverless and self-hosted Node alike, no WebSocket server required.
Link access (documented in the Share dialog since v0.11.7): anyone who
can reach your instance can open a share link; ownerless designs (created
while signed out) are editable by everyone with the link, while designs
owned by an account open read-only for everyone else — there is no cloud
gatekeeping, reachability is decided by your deployment.
Optimistic local edits always keep the canvas butter-smooth. Since v0.10
collaboration is also offline-safe: edits made while disconnected (or
after a crash/reload) queue in IndexedDB and merge deterministically on
reconnect — every element is a hybrid-logical-clock-stamped last-write-wins
register with delete tombstones, and the server merges divergent autosaves
with the same pure rule every client runs, so no one's work is ever
silently overwritten (see
research/OFFLINE-MERGE.md) - Comments & annotations — a first-class comments mode: click anywhere on the canvas (or on an element — the pin follows it) to drop a threaded comment. Replies, resolve/reopen, delete-own, unread badges, collaborator avatars and live propagation to everyone in the design
- Animations — Magic Animate — an Animate panel (Page / Element tabs) with 9 element animations (fade, rise, pan, pop, wipe, zoom, rotate, breathe), speed presets, duration/delay/easing/direction controls, and page transitions (fade/slide/morph, 0.1–2.5 s). One-click Magic Animate gives the whole page a tasteful animation pass. Animated playback in Preview with autoplay and a reduce-motion accessibility toggle
- SVG export — true vector export: text, shapes, paths, gradients, opacity, transforms, images and shadows serialize to clean, sanitized SVG (no rasterization of vector elements, opens in browsers and vector editors)
- Video export (MP4/WebM) — a real rendering pipeline: the animation timeline is rendered frame-by-frame to an offscreen canvas and encoded locally via MediaRecorder — MP4 (H.264) where the browser can encode it, WebM otherwise. Honest format detection is shown before you export. No uploads, no server rendering. Since v0.11 the page's audio is muxed into the recording (best-effort per browser) and beat-synced pulses render into the video
- Audio/video elements & Beat Sync (v0.11) — upload MP3/WAV/MP4/WebM
through the chunked media store (works within every serverless body limit);
audio renders as a waveform card with the tempo detected in-browser, video
as a poster frame. Media plays in Preview/Present, and any element can
pulse on the music's beats — in the editor preview, presentation and
exported video. See
research/MEDIA-AND-BEAT-SYNC.md - AI Assistant 2.0 — the assistant is now design-aware: it sees your page's elements, colors, fonts and selection, and answers with structured, validated actions ("move the title to the top", "change all heading colors to purple", "make the layout more balanced", "add a CTA"). Every change lands as a normal, undoable edit
- AI provider abstraction — all AI features route through one server-side provider layer: per-IP rate limits, usage accounting, capability probing and graceful degradation when no provider is configured (local features like background removal, palette generation and layout balancing keep working; the rest show a clear "requires an AI provider" notice)
v0.4.0 — the Magic Suite: AI editing, PostgreSQL & infrastructure
- PostgreSQL database — Prisma migrated from SQLite to Postgres (native
jsonbdocuments); one database technology everywhere, local & serverless-ready (Neon / Supabase / RDS). Includes the.env.examplePostgres URL guide - Magic Resize — Canva Magic-Switch-style resize dialog: check any of 12 channel presets (Story / Reels / posts / A4 / poster / business card…), then resize in place or Copy & resize — every copy gets an automatic smart re-layout (elements scale, keep their relative positions, fonts & strokes re-proportion)
- BG Remover — one-click background removal on any selected image, powered by an in-browser ONNX segmentation model (real alpha cut-outs, first use downloads the model once, then it's cached)
- Magic Eraser — describe an unwanted object ("the person in the background") and AI removes it, reconstructing the background behind it
- Enhance image — one-click AI photo cleanup: sharpening, lighting and colour balance while keeping the framing identical
- Translate design — File → Translate: pick from 24 languages, and every text across all pages is AI-translated in place (layout intact, one undo)
- Canvix AI assistant — a chat copilot app (Canva AI-style) that sees your design: ask for headlines, palettes or taglines and tap the suggested actions to add text, generate images, save palettes or translate the design
- Live photo search — the Photos panel now searches real web photos (not just the bundled library) with debounced live results
- Server-side version history — snapshots now persist in Postgres (30 per design, cascade-deleted), with localStorage as an offline fallback
- Dashboard trash — deleting a design moves it to a Trash section (restore / delete forever), plus a Recent/Name sort control
- 5 new document templates — Minimal Resume, Certificate of Achievement, Clean Invoice, Monthly Calendar and Business Card under a new "Docs & office" category (21 templates total)
v0.3.2 — fonts, gradients, PDF, photos & AI
- 63-font library — 15 → 63 families across five categories (sans / serif / display / handwriting / new Mono) in a searchable, pill-filtered Canva-style font dropdown
- Gradient fills — linear & radial gradients on all shapes and text, edited straight in the colour menu: preset swatches, Linear/Radial toggle, 0-360° angle slider, from/to colour pickers, one-click remove
- PDF export — print-ready, multi-page PDF downloads straight from the browser (jsPDF, zero watermarks)
- Photos tab — new side panel with a bundled, license-free stock library (7 categories × 6 AI-generated photos), tag search, category chips and click-to-place
- Magic Write — AI copywriter app: describe what you need, pick kind (headline / tagline / body / caption) and tone, get 3 on-brand options, add one to the canvas
- AI image generator — prompt + style + aspect ratio → a design-ready image on your canvas
- Design Trends 2026 templates — five templates from Canva's official 2026 trend report: Prompt Playground, Notes App Chic, Texture Check, Opt Out Era and Granny Wave
- Per-page guides — ruler guides now belong to their page; snapping and Clear-guides act on the current page only
v0.3.1 — editor completeness: rulers, crop, layers rename, version history
- Rulers & manual guides — Canva-style ruler strips (top/left) with adaptive tick
ladders and pointer markers; drag out of a ruler to pull a
#9954FFguide, drag it to move, drag off-page or double-click to delete, and snap elements to it. Toggle with Shift+R or the File menu - Image crop — select an image → Crop button (or right-click → Crop image): drag handles or draw a brand-new crop region, lock to aspect presets (1:1, 4:3, 16:9…), rule-of-thirds overlay, live pixel readout, flips baked in
- Layer renaming — double-click any row in the Layers list for an inline rename; custom names persist with the design
- Version history — File → Version history (or Ctrl+Alt+S): labelled local snapshots per design with restore (undo-able), delete and relative timestamps
- Group rotation fixed & refined — rotating a group no longer double-rotates its children, and ungrouping a rotated group keeps every child exactly where it was
- Clickable app tiles — app cards and chart rows open on any click (not just the Open/Add button), with full keyboard support
v0.3.0 — deep visual parity & feature expansion
- Canva-exact selection chrome — selection border and handles re-skinned to the
measured purple
#7630D7with white circular anchors, 2px border and rotation snapping - Alignment guides recolored to the measured guide purple
#9954FFat 2px - Context toolbar in the topbar — selecting an element swaps the topbar middle for a Canva-style toolbar (32px / r10 buttons): font dropdown with live previews + search + category pills, size stepper, colour menu, B/I/U/S/Uppercase, alignment, spacing, transparency, effects and position
- Canva-style colour menu — spectrum + eyedropper + current colour, Brand Kit swatches, 4×7 default solids and gradient swatch rows
- Text effects: 10 presets — Drop, Lift, Glow, Hollow, Outline, Background, Splice, Neon and Echo in a 3-column thumbnail grid
- Position popover — 2×2 layer ordering (forward/backward/to front/to back), 6 align-to-page tiles and W/H/X/Y/Rotate numeric fields with ratio lock
- Right-click context menus — element & page variants with keyboard shortcuts, layer ordering, group/lock/hide and delete (244px, 32px items, 13px)
- Groups — Ctrl+G / Ctrl+Shift+G group & ungroup; children move and scale together
- Marquee selection — drag on empty canvas to box-select; space+drag or middle-mouse to pan; Ctrl+A select-all
- Zoom dropdown — 300%→10% presets plus Fit & Fill (128px listbox, 40px items)
- Expanded elements library — search, basic shapes, polygons (pentagon/hexagon/ octagon/diamond/semicircle) and 11 new decorative graphics & icons
- Media upgrades — image brightness/contrast/saturation adjustments, flips, replace-image flow, set-image-as-background and drag-and-drop onto the canvas
- Templates panel — search, category chips, recommended section, skeleton loading and a full-size preview modal with one-click apply
- Brand kit 2.0 — colour palettes, logo uploads, and one-click “apply brand style to page” (nearest-colour mapping + font swapping)
- Apps ecosystem — a registry-driven plugin panel: Charts, QR generator, Icons,
Colour palette generator, Placeholder text and Stickers. New apps register in
apps/registry.tsx - Dashboard polish — account avatar menu, help & shortcuts modal (Get help FAB), skeleton loading states, and toast notifications
- Mobile contextual action bar — selecting an element on phones swaps the bottom navigation for text/shape tools
- New keyboard shortcuts — group/ungroup, select-all, lock, layer ordering (
]/[)
v0.2.0 — the Canva-accurate dark refresh
- Canva-2026 dark theme — every surface re-skinned from live canva.com research:
deep-purple app background
#1F142E, editor workspace#0F1015, rail#1D1F26, cards#16171Dand the signature cyan→purple gradient topbar - New 8-tab editor rail (72 px, exactly like Canva): Templates · Elements · Text · Brand · Uploads · Tools · Projects · Apps
- New topbar: File menu, Resize, Editing/Viewing mode toggle, “All changes saved” pill, Preview, Share (white Canva pill) and Download
- Text effects — Shadow, Lift, Hollow, Neon and Echo presets from the context toolbar
- Draw tool — freehand brush (color + size) committed as editable vector strokes
- Brand kit — save brand colors & fonts, apply them to any selection (localStorage)
- Charts — bar, line and donut charts built from native editable elements (Apps panel)
- Preview mode — fullscreen presentation with keyboard navigation
- Share dialog — copy link + access levels (local-first)
- Resize dialog — preset sizes or custom, elements stay centered
- Canva-style home — “What will you design today?” hero, left rail with purple Create button, brand-kit section, and the purple “Get help” FAB
Core (v0.1.0)
- Drag-and-drop editor — move, resize and rotate text, shapes, lines, graphics, stickers and images on a smooth HTML5 canvas (Konva.js)
- Typography — 15 bundled Google Fonts, bold/italic/underline/strike, letter & line spacing, alignment, colors
- Smart snapping guides — elements magnetically align to the page center, edges and other elements
- Layers panel — reorder, lock, hide and delete any element
- Pages — multi-page designs (great for decks), duplicate & reorder pages
- Templates — 11 starter templates: Instagram posts & stories, pitch decks, posters, logos, YouTube thumbnails
- Backgrounds — solid colors, curated palettes and gradient presets
- Uploads — drag-and-drop your own images (kept locally in your session)
- Undo / redo — full history stack with keyboard shortcuts
- Autosave — designs persist to the database automatically
- Export — PNG & JPG at 1× / 2× / 3× quality, current page or all pages, generated 100% client-side
- Responsive — the same editor adapts to desktop, tablet and mobile with touch gestures (pinch-zoom, drag-to-pan)
🚀 Quick start
Prerequisites: Node.js 20+ (or Bun), and a PostgreSQL database — a free Supabase project is the easiest start (any other Postgres works too: Neon, RDS, Docker, local).
git clone https://github.com/mir-ashiq/canvix.git
cd canvix
npm install # or: bun install
# set up the database (get a connection string first — see below)
cp .env.example .env # then edit DATABASE_URL
npm run db:setup # migrations + 110 free templates, in one command
npm run dev # → http://localhost:3000
Getting your Supabase connection string: Supabase dashboard → Connect (or Project Settings → Database) → copy the URI. Both styles work out of the box — Canvix auto-applies the right flags:
| Style | Looks like | Notes |
|---|---|---|
| Transaction pooler (recommended) | …pooler.supabase.com:6543/postgres |
IPv4-friendly; Canvix auto-adds pgbouncer=true&connection_limit=1 |
| Direct connection | db.<ref>.supabase.co:5432/postgres |
IPv6-only on most plans — use it only if your host has an IPv6 route |
Replace [YOUR-PASSWORD] with your database password, and you're done. If
anything looks off later, open /api/health on your server — it checks
env → connectivity → migrations → seed and prints the exact fix.
Production build:
npm run build
npm run start
☁️ Deploy — Vercel, Netlify, Cloudflare Workers, or your own box
Canvix runs on any Node host and — since v0.9.3 — on Cloudflare Workers too, and — since v0.9.4 — on Netlify. Same codebase, same database, zero config divergence.
Running it long-term? Two operations guides shipped with v0.12.4:
docs/BACKUP-AND-RESTORE.md — the
one-command pg_dump backup, a restore drill executed and verified
step-by-step (counts + bytea sizes + booting the app against the
restored database), scheduled-backup cron, and the media orphan GC —
and docs/UPGRADING.md — the routine upgrade
path, jumping minors, and rollback guidance including the honest
forward-only-migrations caveat.
Vercel (or any Node host)
- Import the repo, set
DATABASE_URL(the transaction pooler URL —…pooler.supabase.com:6543— Vercel functions have no IPv6 route). npm run db:setuponce from anywhere with DB access (migrations + templates), then deploy.postinstallregenerates the Prisma client on every build.
Cloudflare Workers (free tier works)
npm install # adds @opennextjs/cloudflare + wrangler
cp .dev.vars.example .dev.vars # put your pooler DATABASE_URL in it
npm run preview # builds & boots the real worker locally
# ship it:
npx wrangler secret put DATABASE_URL # paste the pooler URL when asked
npm run deploy # → https://canvix.<you>.workers.dev
# then open /api/health on the deployed URL — {"ok":true} = full stack green
Git-connected builds (Workers Builds): just connect the repo and keep the defaults — build command
npm run build, deploy commandnpx wrangler deploy. Since v0.11.2 the build script detects the Workers Builds environment (WORKERS_CI=1) and produces the OpenNext worker output (.open-next/) thatwrangler deployneeds; on every other platform the same command builds the standard standalone output. SetDATABASE_URLas a build/secret variable (the transaction pooler URL) and deploys go green end-to-end.
Local preview note:
npm run previewis great for UI and no-DB routes, but some environments' localwrangler devcannot open the Postgres TLS upgrade (a workerd runtime quirk — details inresearch/CLOUDFLARE-WORKERS-PORTABILITY.md). The real deployment is the source of truth: deploy (free), hit/api/health.
Netlify (free tier works)
Zero-config via Netlify's OpenNext-based Next.js runtime — the repo ships
netlify.toml + a pinned @netlify/plugin-nextjs, so connecting the repo
in the Netlify UI is all it takes:
- Push the repo to GitHub, then Add new site → Import an existing
project in the Netlify UI (build settings are read from
netlify.toml). - Set
DATABASE_URL(the transaction pooler URL) under Site settings → Environment variables — optionallyZAI_BASE_URL/ZAI_API_KEY. - Deploy. Open
/api/health—{"ok":true}= full stack green.
Functions run on AWS Lambda (full Node API surface — scrypt auth, pg
sockets, sharp PNG export all native). To simulate the exact build
locally: npx netlify-cli build --offline (no account needed) — the
function bundle lands in .netlify/functions/ (20.8 MB zipped, well
under the 50 MB limit; v0.9.4's trace diet cut it from 70 MB).
Honest free-tier notes: 100 GB bandwidth/month, 125 K function invocations/month, 300 build minutes — comfortable for personal use.
What makes it work (all already wired):
| Concern | How |
|---|---|
| Next.js on Workers | @opennextjs/cloudflare adapter (open-next.config.ts, wrangler.jsonc) |
| Prisma without native binaries | rust-free client (WASM query compiler) — the wasm is ESM-imported on Workers (compiled at deploy) and read from disk on Node; ships once, keeping the worker at 2.1 MB gzipped |
| Postgres sockets | pg over pg-cloudflare's socket layer (nodejs_compat) straight into the Supabase transaction pooler |
| Auth (scrypt) | workerd implements node:crypto scrypt — same hashes on every runtime |
| SSE collaboration | pure web streams + timers — no Node APIs |
| MCP fetch tool | SSRF guard switches to DNS-over-HTTPS validation + public-only fetch() (global_fetch_strictly_public flag) |
| PNG export | needs sharp (native) — on Workers it degrades gracefully: the tool answers "use SVG instead" |
Honest free-tier notes: Workers free allows 100k requests/day, ~10 ms CPU
per request and a 3 MB (gzipped) worker bundle — fine for trying Canvix
and light self-hosting; heavier editing sessions (scrypt logins, big SSE
pumps) fit the $5 paid plan better. Prisma's rust-free client keeps the
bundle ~85 % smaller than the classic engine, which is what makes it fit
at all. Full details: research/CLOUDFLARE-WORKERS-PORTABILITY.md.
🚦 Rate limiting (v0.12.3)
Every mutating surface carries a per-IP request limit, and the collaboration stream carries a per-IP concurrent-connection cap:
| Surface | Cap (per IP) |
|---|---|
POST /api/designs (create) |
30 / min |
PUT /api/designs/<id> (autosave) |
300 / min — autosave itself fires ≤ ~37/min |
DELETE /api/designs/<id> |
60 / min |
POST …/collab/<id>/events |
600 / min — active clients flush 150/min each |
POST …/collab/<id>/presence |
120 / min — heartbeats are 4/min per client |
POST …/comments |
60 / min |
POST /api/media (upload init) |
30 / min |
GET …/collab/<id>/stream (SSE) |
6 concurrent connections |
Rejected requests get 429 with a Retry-After header and a
RATE_LIMITED JSON body. The limits exist to stop abuse and runaway
loops, never to throttle a working editor — every cap has at least 4×
headroom over the heaviest legitimate client.
Multi-instance fleets — the honest limitation. Counters live in
memory, per server instance (the /api/mcp limiter always worked this
way). A fleet of N instances behind one load balancer enforces each cap
per instance, not per deployment: an abuser rotating across instances
gets roughly N× the budget. The shared-store upgrade (Postgres or Redis
counters) is deliberately deferred past 1.0 — it costs a round-trip on
every mutation and matters only once you outgrow a single instance.
Also: the client IP comes from x-forwarded-for / x-real-ip — the
headers every reverse proxy and hosting platform sets. A bare
next start with no proxy in front exposes no client address, so such
deployments fall back to one shared bucket (localhost is trusted
anyway; the caps still stop runaway loops).
Knobs (in .env): CANVIX_RATE_LIMITS=off disables every cap (self-
hosters behind their own throttle, load tests); CANVIX_SSE_MAX_PER_IP
tunes the stream cap (default 6). Prove the caps on a scratch instance:
BASE_URL=http://localhost:3000 bash scripts/rate-limit-probe.sh.
📡 Observability (v0.12.5)
Three primitives, all off by default — a design tool's server logs are the self-hoster's to read:
- Structured request logging — set
CANVIX_LOG=requestsand every API request emits one JSON line on stdout: method, path, status, duration ({"ts":…,"level":"info","event":"http_request","method":"PUT", "path":"/api/designs/abc","status":200,"duration_ms":18.4}). Query strings are never logged (they carry design ids and cursors); IPs aren't either. Grep-able, JSON-parseable, zero dependencies. /api/health?v=2— the observability payload: aggregate counts (live designs, users, finalized media count and bytes, version snapshots, comments), a DB latency probe, and the app version + uptime. Counts only, never ids — safe to point monitoring at. Plain/api/healthkeeps its v1 shape (plus an explicitv: 1) for liveness probes that should stay cheap.- Error webhook — set
CANVIX_ERROR_WEBHOOK_URLand every unhandled request error (any route, page or server action — via Next'sonRequestErrorhook insrc/instrumentation.ts) POSTs a JSON report: message, path, method, route type, app version, timestamp. 5-second timeout, fire-and-forget — a broken webhook never takes the error path down with it.
🛠 Tech stack
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router) + React 19 + TypeScript |
| Styling | Tailwind CSS 4 + shadcn/ui |
| Canvas engine | Konva.js via react-konva |
| State | Zustand |
| Database | Prisma ORM + PostgreSQL (jsonb) |
| Realtime | Postgres event log + SSE (no WebSocket server needed) |
| AI | server-side provider layer (z-ai-web-dev-sdk, vision-capable) |
| Fonts | 63 hand-picked Google Font families |
| Tests | Vitest (unit) + Playwright (browser) + scripts/mcp-e2e.sh (38 live MCP assertions) |
🧪 Testing
npm test # unit tests (SSRF guard, JSON-RPC conformance) — no DB needed
npm run typecheck # tsc --noEmit
npm run lint # eslint (0 errors, 0 warnings)
# browser smoke: landing → dashboard → editor canvas
# (boots `npm run dev` itself; needs a reachable DATABASE_URL)
npm run test:ui
BASE_URL=http://localhost:3000 npm run test:ui # …or reuse a running server
# MCP end-to-end: 38 assertions over the live transport — auth, initialize,
# tool catalogue, a real create→edit→export workflow, live SSRF-guard
# rejections, and transport hardening (413 body cap included)
CANVIX_MCP_TOKEN=<your-token> npm run test:mcp
📁 Project structure
src/
├── app/ # Next.js App Router
│ ├── page.tsx # single-page app shell (landing / dashboard / editor + ?design= deep links)
│ └── api/ # REST endpoints: designs, templates, collab (SSE), comments, ai
├── components/
│ ├── landing/ # marketing page
│ ├── dashboard/ # home screen: presets, templates, recent designs
│ └── editor/ # the design editor
│ ├── canvas/ # Konva stage, element renderers, collab overlay, comment pins, export bridge
│ └── panels/ # templates / elements / text / brand / uploads / photos / tools / projects / apps / animate
├── lib/
│ ├── ai/ # v0.5 AI provider abstraction (config, rate limits, usage)
│ ├── collab/ # v0.5 realtime: protocol, client session, participant identity
│ ├── magic-layers/ # v0.5 image → editable layers pipeline
│ ├── animations.ts # v0.5 animation engine (pure functions of time)
│ ├── svg-export.ts # v0.5 vector export
│ ├── video-export.ts # v0.5 MediaRecorder MP4/WebM pipeline
│ └── design-actions.ts # v0.5 validated, undoable AI editor actions
├── store/ # zustand stores (app + editor + comments)
└── hooks/
prisma/ # schema + seed
research/ # architecture decision records & audits
🤖 AI provider setup
Canvix works fully without any AI provider — the editor, templates, exports, collaboration and comments need nothing but the database. AI features (Magic Layers, Assistant, Magic Write, image generation/editing, translate, photo search) activate when a provider is configured on the server:
Option A — environment variables (recommended for Vercel etc.):
# .env (server-side only — never exposed to the browser)
ZAI_BASE_URL=https://your-provider.example/v1
ZAI_API_KEY=your-key
Option B — a .z-ai-config JSON file on the server host (checked at the working
directory, then ~, then /etc):
{ "baseUrl": "https://your-provider.example/v1", "apiKey": "your-key" }
The provider layer (src/lib/ai/provider.ts) resolves credentials in that order,
probes capabilities at runtime and answers /api/ai/capabilities (booleans only —
never secrets). When no provider is configured, AI features degrade gracefully:
background removal (local ONNX), color palettes and layout balancing keep working,
and provider-backed features show a clear setup notice. Every AI route is rate-
limited per IP and logs usage into the AIUsage table.
Local-only vs provider-backed features
| Feature | Works without a provider? |
|---|---|
| Editor, templates, uploads, draw, groups, rulers, crop | ✅ always local |
| Exports — PNG / JPG / PDF / SVG / video | ✅ always local |
| Collaboration, presence, comments | ✅ only needs the database |
| Background remover | ✅ local (in-browser ONNX) |
| Color palettes, layout balancing | ✅ local (deterministic) |
| Magic Layers, AI Assistant, Magic Write | ⚙️ needs a server AI provider |
| AI image generation / eraser / enhance | ⚙️ needs a server AI provider |
| Translate, live photo search | ⚙️ needs a server AI provider |
| MCP server (agent tools) | ✅ with token set — generate_design additionally needs the AI provider |
🤖 AI agents (MCP)
Canvix ships a Model Context Protocol server — the same interoperability standard Canva's “AI Connector” uses. It turns your Canvix instance into a set of tools an AI assistant can call: create designs, add elements, restyle, animate, comment, export.
Enable it
# 1. set a secret on the server (openssl rand -hex 24)
CANVIX_MCP_TOKEN=your-long-random-secret
# 2. restart Canvix — the dashboard "AI agents (MCP)" card flips to Enabled
Connect a client
Endpoint: https://your-canvix.example.com/api/mcp (Streamable HTTP, stateless).
Client config (Claude Desktop / Cursor / any MCP client that supports remote
HTTP servers):
{
"mcpServers": {
"canvix": {
"url": "https://your-canvix.example.com/api/mcp",
"headers": { "Authorization": "Bearer <CANVIX_MCP_TOKEN>" }
}
}
}
Tool surface (20 tools)
| Area | Tools |
|---|---|
| Discover | get_capabilities, list_templates, search_designs, get_design, get_design_content |
| Create | create_design (blank or from template), generate_design (AI) |
| Edit | add_text, add_shape, add_image, add_table, add_embed, set_background, update_element, delete_element, animate_page |
| Export | export_design (svg / png / json) |
| Collaborate | comment_on_design, list_comments |
| Reference | list_fonts |
Every write tool validates and clamps its arguments (hex colors, coordinates,
font whitelists, size caps) and appends a pages:replace event to the design's
collab log — so edits by agents appear live in any open editor session, and
humans can undo or keep refining them.
generate_design asks the server's AI provider for a structured page layout,
normalizes it into native elements (clamped sizes, whitelisted fonts, element
caps) and saves a real, editable design — not a raster. Without a provider
configured, the tool returns an honest setup error; template-based creation
keeps working.
PNG export rasterizes server-side with sharp (system fonts render the text); SVG is true vector with font-family references — both caveats are stated in the tool descriptions so agents pick the right format.
🔐 Security notes
- Security headers + CSP baseline (v0.12.1) — every response from every
route class (pages, APIs, SSE, static assets) now carries
X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,X-Frame-Options: SAMEORIGIN(+frame-ancestors 'self'),Strict-Transport-Security(180 days) and aPermissions-Policythat turns off camera/mic/geo. The CSP ships as Report-Only so nothing breaks while violations are reviewed: it allowlists Google Fonts,data:/blob:images, blob workers for the background remover and TLS-only frames for embed cards. After a review week, flip theContent-Security-Policy-Report-Onlyheader innext.config.tstoContent-Security-Policy(and tightenframe-srcto the embed allowlist). HSTS note for self-hosters: browsers only honour it over TLS, so anhttp://deployment is unaffected by definition — the header starts counting the moment you put the instance behind HTTPS. A Playwright spec (tests/e2e/security-headers.spec.ts) asserts the whole baseline on every route class in CI - Secrets never reach the browser — provider credentials are resolved server-side only; the public capabilities endpoint exposes booleans only
- Validated collaboration ops — every realtime event is type-checked,
size-capped (256 KB) and dropped (never crashed) if malformed; see
research/V05-SECURITY-AUDIT.md - Sanitized SVG export — all text is XML-escaped, path data is whitelisted,
image hrefs only allow
data:/https:; no scripts or event handlers - AI actions are double-validated (server clamps + client re-checks) and always undoable
- MCP endpoint hardening — disabled until
CANVIX_MCP_TOKENis set; Bearer auth with timing-safe compare; cross-origin requests rejected (DNS-rebinding guard per the MCP spec); 120 req/min/IP; SSRF-guarded image fetches (private hosts denied, 8 MB/10 s caps); tool arguments whitelisted & clamped per element type; agent writes ride the same validated event log as human edits - Designs without an owner are link-editable by anyone who can reach
the instance (deliberate — the Share dialog documents this); owned
designs are read-only for everyone else, enforced on both the document
rail and (since v0.11.8) the collaboration events rail; see the
security audit and
research/V0118-ROADMAP-RESEARCH.md§3.7
🧭 Roadmap
- Canva-accurate dark theme & 8-tab editor rail (v0.2.0)
- Text effects, draw tool, brand kit, charts, preview & share (v0.2.0)
- Canva-exact selection, context menus, groups, colour menu, apps ecosystem (v0.3.0)
- Rulers & manually placed guides, image crop, layer renaming, version history (v0.3.1)
- 63-font library, gradient fills, PDF export, stock photo library, AI apps (v0.3.2)
- Magic Suite: AI image editing, Magic Resize, Translate, PostgreSQL (v0.4.0)
- Magic Layers, real-time collaboration, comments, animations, SVG & video export, AI Assistant 2.0 (v0.5.0)
- MCP server (20 agent tools), tables, frames, curved text, embed cards (v0.6.0)
- Rendering performance (structural sharing, memoized nodes, O(1) undo) & the premium collection (v0.7.0)
- 110-template library, Supabase-first migrations & self-diagnosing setup (v0.8.0)
- Accounts & per-design ownership — free username/password accounts, private designs, one-click migration of existing work, SSRF DNS pinning (v0.9.0)
- Claim eligibility (browser-attributed adopt-all, per-design adopt), CSRF same-origin enforcement, pre-Phase-4 verification gate (v0.9.1)
- Yjs-grade offline merge for collaboration — HLC-stamped element LWW + delete tombstones, persisted offline doc/op-queue (IndexedDB), server-side merge on divergent autosaves (v0.10.0)
- Audio/video elements & Beat Sync — chunked media store (bytea + Range streaming), in-browser tempo/beat analysis, per-element beat pulses in preview/present/export (v0.11.0)
- Production hardening sweep — presence P2002 storm + 30s function hangs, v0.11.0 smoke (preview playback, export audio, offline detection), editor Projects panel crash, Elements panel tab strip (v0.11.1–v0.11.3)
- Canonical design URLs —
/design/<id>share links, address-bar sync, legacy/?design=links self-heal to the canonical path (v0.11.4) - Presence join latency fix — per-tick presence diff + announce-on-join: a new collaborator shows up for idle clients in ≤ 3.2 s (was 60–90 s), remote cursors included (v0.11.5)
- Panel state hygiene — Photos category tabs exit search mode (no more stale 6-of-42 grid), web search without an AI provider says so, and the top-bar mode control + Tools-panel Draw toggle always agree (v0.11.6)
- QA sweep close-out — empty-canvas click deselects (Canva parity), zero render-phase store writes in canvas gestures (the “Cannot update a component while rendering” warning, root-caused to stroke/marquee/guide commits — not Magic Layers), and the Share dialog documents the real access model for self-hosters (v0.11.7)
- Collab-rail ownership enforcement — the events/presence routes now apply the same design-guard rule as the document rail (owned designs 403 non-owners; ownerless designs stay link-editable), plus same-origin enforcement and design-gated presence writes (v0.11.8)
- CI gate — every pull request now runs
tsc --noEmit+ eslint + the full vitest suite on GitHub Actions, and merges tomainalso run the Playwright browser suite against a real Postgres service container — the deploy pipelines finally get tested code (v0.12) - Security headers + CSP baseline —
nosniff,Referrer-Policy,X-Frame-Options/frame-ancestors, HSTS andPermissions-Policyon every route class from one source of truth innext.config.ts, a report-only CSP allowlisting Google Fonts +data:/blob:images + blob workers, and the strict per-manifest CSP the v0.13 plugin serve route will use, drafted and unit-tested insrc/lib/security/plugin-csp.ts(v0.12.1) - Error boundaries — a top-level boundary around the app shell plus
per-surface boundaries around the editor and dashboard: a render-time
crash now shows a recovery UI (reload editor / back to dashboard /
copy diagnostics with design id + last saved seq) instead of a white
screen;
reactStrictModere-enabled after a full-suite re-evaluation (v0.12.2) - Rate limits & connection caps — per-IP limits on designs CRUD,
collab events/presence, comments and media init (429 +
Retry-After, every cap ≥ 4× the heaviest legitimate client), SSE streams capped at 6 concurrent connections per IP, a load-probe script that proves both caps on a live instance, and an honest “multi-instance fleets” section in the README (v0.12.3) - Docs & ops runbook —
docs/BACKUP-AND-RESTORE.md(pg_dump backup with the bytea note, a restore drill executed + verified end-to-end: counts, media bytes, booting the app on the restored database, scheduled-backup cron) anddocs/UPGRADING.md(routine upgrades, jumping minors, rollback with the forward-only-migrations caveat), plus a media orphan GC script with its own verified drill (v0.12.4) - Observability floor — opt-in structured request logging
(
CANVIX_LOG=requests, one JSON line per API request, no query strings, no IPs),/api/health?v=2with aggregate counts + DB latency probe (never ids), and an optional error webhook (CANVIX_ERROR_WEBHOOK_URL, POSTed from Next'sonRequestErrorhook for every unhandled error) — all off by default for self-hoster privacy (v0.12.5) - Plugin system (external, installable apps) — sandboxed-iframe MVP
per
research/PLUGIN-SYSTEM.md: manifest + permission model, iframe sandbox with postMessage bridge, an “Apps” panel store, and 2–3 reference plugins (stock icons, charts, QR codes) to prove the surface — then a community plugin registry. In progress: the security-boundary core (P1) landed insrc/lib/plugins/— manifest validator (id/api/engines/entry/ permissions/element-schema rules), versioned postMessage RPC envelope with typed error codes, the default-deny permission catalog for the consent screen and broker, plugin data caps at collab parity, and the accept-or-reject renderer-SVG sanitizer — all pinned by a protocol-level security regression suite (20 hostile manifests, 16 hostile SVGs, RPC replay/spoof corpus) (v0.13.0); the install rail (P2) followed —CANVIX_PLUGINS(default off) gates/api/pluginsinstall/list/uninstall + a DB-served asset route with the per-manifest CSP, sha256-pinned bundles fetched through the SSRF guard (v0.13.1); the panel runtime (P3) turned installs into running code — the RPC broker (origin discipline by window identity, one-time handshake token, 200 calls/min, default-deny dispatch), a lossy read-only design-snapshot projection, namespaced 256 KB plugin storage, the<iframe sandbox="allow-scripts">panel host, and the Apps grid's "Installed" section with a consent modal that lists every requested permission verbatim (v0.13.2); the element & renderer contract (P4) let plugins put their OWN element types on the canvas — one newpluginunion member whose per-plugin variety lives in cappeddata, a service iframe per plugin-with-elements answering host→pluginelements.rendercalls (10 s deadline, sanitized ≤ 2 MB SVG or raster output, cached by element+data+size), placeholder cards for every §4.9 state (flag off / not installed / disabled / not responding), export coverage (SVG embeds the sanitized vector; PNG/JPG/PDF/video rasterize it), a host-rendered schema properties card, and install-scopedaddPluginElement/updatePluginElementDataactions whose caller identity comes from the install row — verified end-to-end that uninstall/crash/flag-off never loses document data (v0.13.3) - Mobile editor polish — touch-first selection handles, gesture zoom/pan without modifier keys, bottom-sheet property panels (the responsive groundwork from v0.11.3 makes this tractable)
Path to v1.0 — the full phased plan (current-state analysis, 2026
landscape research, prioritized epics with effort estimates, risk
register) lives in research/V0118-ROADMAP-RESEARCH.md. The arc:
v0.12 trust & rails (CI gate, security headers, rate limits, ops
runbook) → v0.13 plugin system MVP → v0.14 daily-driver editor
depth (persistent uploads, clipboard, folders, filters, charts) →
v0.15 rich content & export (rich text, WebCodecs video, vector
PDF) → v0.16 mobile & collaboration scale (touch-first editing,
LISTEN/NOTIFY fan-out) → v1.0 = every promise on this page shipped
and verified.
🤝 Contributing
Contributions are very welcome! Areas that especially need help:
- New templates (add them in
src/lib/templates/collection-v08-*.ts) - New fonts, sticker packs, shape graphics
- Mobile UX polish
- Tests
- Fork the repo & create a branch
- Make your change (
bun run lintshould pass) - Open a pull request
⚖️ License
MIT © 2026 Ashiq Hussain Mir
Canvix is an independent open-source project and is not affiliated with, endorsed by, or connected to Canva.
Explore related projects
freeCodeCamp/freeCodeCamp
freeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.
458,826 starsdonnemartin/system-design-primer
Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards.
377,475 starsreact/react
The library for web and native user interfaces.
252,467 starsvuejs/vue
This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
212,625 stars