Documentation

What it is

DrawFiddle 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.

.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 };

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 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.

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:

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

Premium (coming soon)

Premium accounts get a second toolbar button, ★ Premium. What it offers right now:

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.

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=proFiddle.Query.Text("city"). Different URL, different values. Nothing to store, nothing to call.
Fetch dataFiddle.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 tapsFiddle.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&current=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:

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 [email protected].

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

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: [email protected].