Documentation
Last updated 8 September 2026
What it is
DrawnUI Fiddle is an online playground for DrawnUI, a UI toolkit that draws everything on a SkiaSharp canvas. You write C# in the browser; Roslyn compiles it in the browser (WebAssembly, no server round-trip) and the result is drawn live on a canvas. Or you write React: the same engine, ported to TypeScript, drawing on CanvasKit — see React (TSX).
Every widget on the Community page is source code, not a published app. Opening one loads the C# compiler and the .NET runtime once (that is the multi-megabyte payload you see the first time), then every snippet compiles and runs locally. The same code runs unchanged in .NET MAUI, Blazor, OpenTK and other .NET hosts.
The editor
drawfiddle.com/app. Monaco editor on the left, live canvas on the right.
- ▶ Run compiles and renders. HotReload re-runs automatically on every edit once the code has no errors.
- IntelliSense: completion, signatures and error squiggles come from the same in-browser Roslyn.
- Presets (top bar): built-in samples to start from. Each is a plain fiddle you can edit.
- Description (cyan field under the editor): up to 200 characters, shown on share cards, link previews and the Community page. Tags next to it, see Tags.
- Canvas background: the colour you pick travels with the share.
- Keyboard: click the canvas first, then physical keys go to your drawn controls (drawn text editors, games). Keys go back to the code editor when you click it.
- The DrawnUI Fiddle logo returns to the Community landing page without reloading.
.NET (C#)
The default language. Your snippet is the body of a method that returns the control to draw —
no class, no namespace, no using lines to write:
return new SkiaLabel { Text = "Hello", TextColor = Colors.White,
HorizontalOptions = LayoutOptions.Center };
- Already in scope:
System,System.Linq,System.Collections.Generic,DrawnUi,DrawnUi.Draw,DrawnUi.Views,DrawnUi.Controls,AppoMobi.Gestures,AppoMobi.SpecialsandSkiaSharp. Add your ownusingat the top if you need more. - Types are allowed next to the statements: a
class,recordor custom control declared in the snippet is lifted out and compiled beside it, so a sample that needs its own control or effect ports as it is. - Roslyn runs in your browser — the compiler and the .NET runtime are downloaded once (that is the multi-megabyte first load) and every later compile is local. Errors and squiggles come from the same compiler, so they are the errors you would get in an IDE.
- The same code runs unchanged in .NET MAUI, Blazor, OpenTK and other .NET hosts. Export MAUI hands you a ready project with the snippet as its page.
- Published fiddles start fast: the compiled assembly is cached per snippet, so a visitor opening
/p/<id>usually skips the compiler entirely.
React (TSX)
The same fiddle writes React. The C# | React switch in the toolbar changes the language of the snippet: C# is compiled by Roslyn and drawn by DrawnUI on a SkiaSharp canvas, React is compiled from TSX in the browser and drawn by DrawnUI for React on a CanvasKit canvas. One engine, two languages: the controls, the property names and the layout rules are the same, which is why every React preset here is the twin of a C# one.
- The snippet is a module:
export defaulta component (or a ready element). The fiddle owns theCanvasaround it, exactly as it owns the canvas around a returned control in C#. - Tags are the controls:
<SkiaLayout Type="Column">,<SkiaLabel>,<SkiaButton>,<SkiaShape>,<SkiaScroll>… with PascalCase properties (HorizontalOptions,UseCache,Padding). React drives the tree; the engine measures, lays out and draws. - Engine classes for code that builds controls itself (a cell template, for instance) are on
Core:new Core.SkiaLabel({ ... }).useState,useEffectand the other hooks are in scope, andconsole.logreaches the console panel below. - IntelliSense comes from the package's own type definitions, so completion and type errors are the real ones. Type errors are reported as warnings and do not block a run; syntax errors do.
- No .NET is downloaded for a React fiddle. A shared React snippet plays from its compiled module:
the
/p/<id>page starts in about a second instead of loading a runtime and a compiler. - React share ids start with
r— that is how a link knows which engine to boot. Everything else about sharing, embedding, snapshots and the Community is identical. - Export Component is the React counterpart of Export MAUI: it writes a Vite + React project
around the snippet and downloads it as a .zip —
src/App.tsxis your component with the imports the fiddle was providing for you,src/main.tsxboots the engine and owns theCanvas,public/fonts/carries the faces the fiddle draws with. Thennpm install && npm run dev, andnpm run buildgives a static site. Nothing is uploaded; the project is written in your browser. - Not for React snippets: Export MAUI (a .NET project needs C#) and the premium publish options. Export Web, embeds, snapshots and the Community work the same.
The port covers layouts, scrolling with recycled cells, labels, shapes, images, SVG, buttons, toggles,
sliders, progress bars, carousels, drawers, text editors, animations, Lottie and gestures. The
documentation
tracks what is ported and what is still missing; a live gallery runs at
helloreact.drawnui.net. In your own
project it is npm i drawnui-react; here it is already loaded.
Export Web
One menu in the editor toolbar, ⭳ Export Web, for everything you can do with a fiddle. All of it is free; signing in only adds a library entry so you can find your things again.
- Copy source code: the C# to the clipboard, nothing uploaded.
- Share as Fiddle: a short link
drawfiddle.com/f/<id>that opens the code in the editor with a live preview. The id is a hash of the code: same code = same link, always. - Share as App: copies
/p/<id>— the canvas only, full screen, no editor. - Run as App: opens that standalone page in a new tab.
- Embed canvas as iframe: preview + ready-made HTML for the running app, see Embedding.
- Embed source code as iframe: preview + HTML for a light read-only source page (
/c/<id>, syntax highlighted, no WebAssembly). - Download snapshot (JPG / PNG): see below.
- Download sharing preview (JPG): a direct image link to the 1200×630 social card of the canvas (center-cropped). It is also the preview of every share link; hotlink it in a README or chat.
Download a snapshot
Export Web → Download snapshot (JPG) saves the full canvas exactly as rendered right now, at its current pixel size, JPEG quality 95. (PNG) is lossless and keeps transparency. Nothing is uploaded and nothing is added to your library, the file is produced in your browser. Handy for charts, mockups and documentation images: write the snippet, run it, download.
Community
The landing page lists fiddles people chose to publish. Cards show the title, description, author's display name, tags and rating. Click a card for details, then:
- Editor: loads the source into the editor and compiles it in your browser.
- ▶ Run: plays it full screen in a new tab.
- Share: copies the existing share link. Nothing is created or added to your account.
- Rate 1–5 stars (signed in). Ranking is a Bayesian average, so one 5-star vote does not beat ten 4-star ones; unrated items start at 4.
- Report anything that breaks the Terms; a moderator gets notified.
- ‹ › at the sides (or the ← → keys) move to the previous / next card without closing; Esc closes.
Publishing your own: sign in, share it from the editor (Export Web → Share as Fiddle, give it a description), then in Account → My fiddles press Submit to Community. A moderator reviews it before it goes live; you can withdraw at any time, and Unlist later. The Contribute button on the landing page walks you through it.
Tags
Up to 5 tags per fiddle, letters, digits and dashes (chart, dashboard,
game-2d). Set them in the editor next to the description before you share, or later from
the card popup (author or moderator). The Community page filters by tag and shows the most used ones
above the grid.
Account and library
- Sign in with your email: you get a 6-digit code, no password. Paste the code as it arrives.
- Display name (Account tab): shown on Community cards instead of your email, which is never shown.
- My fiddles / My apps: every share or export you make while signed in lands here. Paste a link someone sent you to add it. Filter pills: All · Community · In review · Pinned · Published · AOT.
- Pin (premium): keeps a share alive forever even with zero visitors, so old embeds never break.
- Delete account removes your account, library, ratings, reports and published apps. Shared code stays reachable by its link (it is content-addressed and may be identical to someone else's).
Premium (coming soon)
Premium accounts get a second toolbar button, ★ Premium. What it offers right now:
- Publish pre-compiled app (WASM) — a trimmed standalone web app on its own URL.
- Publish pre-compiled AOT app (WASM) — the same, compiled to native WebAssembly for CPU-heavy code.
- Publish pre-compiled app (Server-Side) — rendered on our server, streamed to the viewer, nothing to download.
Publish pre-compiled app (WASM) builds your fiddle into a trimmed Blazor WebAssembly app on its
own URL, drawfiddle.com/a/<id>/: no editor, no compiler, no badge, only your code
plus DrawnUI and SkiaSharp. The build runs in the cloud and takes a few minutes; progress shows in
Account → My apps.
- Publish pre-compiled AOT app (WASM) compiles the app, DrawnUI and SkiaSharp to native WebAssembly: faster for CPU-heavy code (physics, particles, big loops), at the cost of a larger download (~15 MB vs ~9) and a longer build (~8 min). Plain builds are best for most UI.
- Runtime files are shared between apps and cached at the edge, so a second app on the same DrawnUI version loads fast.
- Quotas: 10 apps, 300 MB of storage, 30 publishes per month. Rebuild or Unpublish from My apps.
Server apps
Publish pre-compiled app (Server-Side) is the third option: publishing compiles the snippet on our
server right away (about a second) — code that is not allowed in server apps is rejected at that moment with
a readable error, nothing is published — and renders the first frame, so the link works instantly for the
first visitor. Then the fiddle runs on our server and the viewer
receives a stream of JPEG frames over a live connection — like a video that reacts to taps. Nothing to
download, no WebAssembly: the page is up in well under a second on any device, even old phones. Best
for charts, dashboards, animated widgets and demos. Frames are sent only while something changes on the
canvas (animations, taps, images arriving) — an idle app costs no bandwidth; the cap is 30 fps. Each
viewer gets their own instance (their own taps, animation from the start); the page shows the app's first
frame instantly while the instance starts. Limits: physical keyboard and drag gestures are not forwarded
yet (taps are), a session ends after 30 minutes of viewing, and the number of viewers rendering at the
same time is limited (a viewer arriving when every slot is busy waits for one). Snippets must stay inside
drawing code: file, network, reflection, threads and similar APIs are rejected with a readable error, web
images are capped at 8 MB each, and every app runs in its own locked-down process with a memory and CPU
budget. The link is live.fiddle.drawnui.net/x/<id>.
URL options: ?w=800&h=500 canvas size in pixels · &fps=15 frame-rate cap (1–30) ·
&embed=1 embed mode (bare image filling the frame, sized to the frame unless w/h are given, no chrome) ·
&debug=1 shows the render stats line (fps, ms per frame, KB/s, cached/compiled). By default the page
shows only the app; errors are always shown.
Embedding a server app: My apps → Embed opens a preview with the HTML to paste — an iframe pointing at
…/x/<id>?embed=1. The app renders at the iframe's size on our server, so the same embed works on any
page without shipping any code to the visitor:
<div style="max-width:800px; aspect-ratio:800/500">
<iframe src="https://live.fiddle.drawnui.net/x/<id>?embed=1"
style="width:100%;height:100%;border:0;border-radius:12px" loading="lazy"></iframe>
</div>
Data in, events out
A published server app is just a URL. Three things connect it to the world, and the same code works in the editor, in a WASM app and in a server app:
| Per-viewer values | …/x/<id>?city=London&plan=pro → Fiddle.Query.Text("city"). Different URL, different values. Nothing to store, nothing to call. |
|---|---|
| Fetch data | Fiddle.Load(url, json => …): the app asks for a public JSON URL; the host fetches it for the sandboxed app and calls back. Weather, prices, your own API — anything https. |
| React to taps | Fiddle.Open(url) sends the viewer somewhere; Fiddle.Emit("buy", data) tells the page that embeds the app what happened. |
1. Values from the URL
var city = Fiddle.Query.Text("city", "Belgrade"); // ?city=London
var count = Fiddle.Query.Int("count", 5); // Int / Double / Bool / Text / Csv / List, with fallbacks
var xs = Fiddle.Query.Csv("values"); // ?values=3,5,8 → [3, 5, 8]
Whoever makes the URL decides the values: a link, your page, your server. Extra parameters on a server app URL
(everything except w h fps embed debug) reach the app unchanged.
2. Fetching data — Fiddle.Load
The app never touches the network itself (server apps have no network access at all — that is the security wall). Instead it asks the host, which fetches on its behalf and hands back the parsed JSON:
Fiddle.Load("https://api.open-meteo.com/v1/forecast?latitude=51.5&longitude=-0.1¤t=temperature_2m",
j => { temp.Text = j.GetProperty("current").GetProperty("temperature_2m").GetDouble() + "°"; temp.Update(); },
err => { temp.Text = err; temp.Update(); });
Rules: https only, public hosts only, 256 KB and 5 s per request, results cached 60 s (a thousand viewers of
the same chart cost one request), at most 32 loads in flight. In the editor and the player the browser fetches
(CORS applies). The Weather preset is the full pattern: ?city=London
→ geocoding → forecast → card, tap to refresh. Try it with different cities in the params box.
3. When the viewer taps something
new SkiaButton("Buy Pro").OnTapped(me =>
{
Fiddle.Emit("buy", new { plan = "pro", price = 9 }); // event out (JSON, max 4 KB)
Fiddle.Open("https://example.com/checkout?plan=pro"); // or: send the viewer somewhere
});
Where it goes depends on how the app is shown:
- Opened directly by its URL (
…/x/<id>):Fiddle.Opennavigates the browser to that URL — a "Buy" button that leads to your checkout needs no JavaScript at all.Emitis logged to the browser console; add&debug=1to also see each event as a toast while testing. - Embedded in your page (iframe): both arrive in your page as a browser message, and your page decides — open a checkout, run any JS,
alert(), re-fetch, navigate. This is the whole integration:
window.addEventListener('message', e => {
if (!e.data || !e.data.fiddle) return; // { fiddle: "buy", app: "<id>", data: { plan: "pro", price: 9 } }
if (e.data.fiddle === 'buy') openCheckout(e.data.data.plan);
if (e.data.fiddle === 'open') location.href = e.data.url; // Fiddle.Open — navigate only if you want to
});
The Data I/O preset is this pattern: three pricing cards, highlighted plan and
currency from the URL, every Buy emits buy. My apps → Embed gives you the iframe HTML with this
listener already in it, and the preview page shows a toast for every event so you can see it work.
Coming next: a stored per-app JSON document (Fiddle.Data) you can PUT through an API
with a per-app key — set the chart's numbers once, every viewer renders them, running instances update live —
plus webhooks and an event log in My apps.
Limits: 20 events per second per viewer, 4 KB per event; URL parameters up to 64 keys / 2 KB each.
Premium is currently assigned by the operator; contact fiddle@drawnui.net.
Embedding
Any fiddle can be embedded on your site with the HTML from Export Web → Embed canvas as iframe:
<iframe src="https://drawfiddle.com/p/<id>" width="600" height="400"
style="border:0;border-radius:12px" loading="lazy"></iframe>
The player boots the .NET runtime inside the iframe (a few MB, cached after the first visit). The share
link itself (/f/<id>) unfurls with the canvas screenshot in chats and social networks.
Limits and lifetime
- Code up to 100 KB per fiddle; description 200 characters; 5 tags.
- Files you add in the Resources panel travel with the share, up to 3 MB per fiddle (25 MB on premium; zipped, your files only — the built-in samples ship with the site and are not stored).
- Shares and sign-in codes are rate limited per hour.
- Shares that nobody visits for 60 days are deleted. Exceptions, kept permanently: fiddles listed on the Community page (or waiting for review), pinned fiddles, and shares made by premium or moderator accounts.
- A free share that carries your own resource files is deleted after 3 days without a visit — storing files costs more than storing code, so those links have to be used. Every visit restarts the 3 days. Premium and moderator shares keep their files permanently.
- Community content is backed up nightly; moderators can export and restore it.
For AI agents and scripts
The editor exposes a small JavaScript API on window.fiddle once loaded, meant for
automation (Playwright, agents):
await fiddle.getState() // { ready, running, status, errors }
await fiddle.setCode(code) // replace the editor code
await fiddle.run() // compile + render: { success, errors, status }
await fiddle.getConsole() // Console.WriteLine output of the snippet
await fiddle.listPresets() // [{ slug, name }]
await fiddle.loadPreset(slug) // load + run a built-in sample
Canvas pixels: take a compositor screenshot of .fiddle-canvas canvas (the WebGL buffer is
not readable through toDataURL).
Connect an assistant (MCP)
The fiddle runs an MCP server, so an assistant can search the community catalogue, read the C# of any snippet and publish code of its own. No account, no key. Add it to Claude Code with:
claude mcp add --transport http drawnui-fiddle https://drawfiddle.com/mcp
Or, in any client that takes a JSON config:
{ "mcpServers": { "drawnui-fiddle": { "type": "http", "url": "https://drawfiddle.com/mcp" } } }
Tools: search_snippets, get_source, create_fiddle,
get_preview. The server card is at
/.well-known/mcp/server-card.json.
Plain text and markdown
Every page that has a text form serves one. Ask for text/markdown, or append
.md:
https://drawfiddle.com/index.md the whole catalogue
https://drawfiddle.com/show/{id}.md one snippet: description, links, source
https://drawfiddle.com/c/{id}.md just the C#
https://drawfiddle.com/openapi.json the public read API
https://drawfiddle.com/llms-full.txt these docs and the DrawnUI skills, inlined
FAQ
Why does the first load download so much? Because your browser receives a real C# compiler and .NET runtime. It happens once; afterwards the files come from cache and the landing page pre-loads them in the background while you browse.
Is my code sent to a server? Compilation is local. Only when you share, export or publish is the source uploaded so the link can serve it.
My app is slow, what can I do? In the editor, the first run after a change includes a Roslyn compile;
that is normal. If the drawn content itself is slow: cache stable subtrees (UseCache), avoid
rebuilding controls every frame, keep effects (shaders, blur, shadows) on small areas. For a published app,
CPU-heavy code (physics, particles, big loops) runs a lot faster as Publish pre-compiled AOT app (WASM);
the plain WASM build interprets .NET code and is best for ordinary UI.
Why isn't my server app buttery smooth? It is not lag — a server app is rendered on our server and sent as a stream of frames over the network, capped at 30 fps and limited by bandwidth, so it can't match a local 60/120 fps canvas for games or non-stop animation. Its strength is the opposite case: charts, static UIs, dashboards, low-animation widgets — they load in well under a second on any device with nothing to download, and cost nothing while idle. For rich animation or games, publish as a WASM app (AOT for heavy code) instead.
Something looks different from MAUI. DrawnUI owns its own layout and rendering on every platform; differences are bugs, report them at the DrawnUI discussions (Fiddle category).
Contact: fiddle@drawnui.net.