{"id":51003993,"url":"https://github.com/modulovalue/dart-live","last_synced_at":"2026-06-20T18:01:49.757Z","repository":{"id":357565446,"uuid":"1236581857","full_name":"modulovalue/dart-live","owner":"modulovalue","description":"The Dart VM compiled to WebAssembly, running in the browser, with stateful hot reload","archived":false,"fork":false,"pushed_at":"2026-05-23T16:50:24.000Z","size":17554,"stargazers_count":11,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-23T18:23:16.542Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://modulovalue.github.io/dart-live/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/modulovalue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-12T11:35:09.000Z","updated_at":"2026-05-23T16:50:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/modulovalue/dart-live","commit_stats":null,"previous_names":["modulovalue/dart-live"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/modulovalue/dart-live","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modulovalue%2Fdart-live","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modulovalue%2Fdart-live/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modulovalue%2Fdart-live/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modulovalue%2Fdart-live/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/modulovalue","download_url":"https://codeload.github.com/modulovalue/dart-live/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modulovalue%2Fdart-live/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34580039,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-20T02:00:06.407Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-20T18:01:49.062Z","updated_at":"2026-06-20T18:01:49.751Z","avatar_url":"https://github.com/modulovalue.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dart Live\n\nThe Dart VM, compiled to WebAssembly, running in your browser, with stateful\nhot reload on the web.\n\n**Live:** https://modulovalue.com/dart-live/\n\n![Dart Live running the canvas-drawing sample](screenshot.png)\n\n## What this is\n\nA single-page app that runs the Dart toolchain entirely in your browser,\nno server. You edit Dart, the CFE compiles it to kernel in the page, the\nVM executes it in WebAssembly, and the analyzer reports diagnostics live\nas you type.\n\n## Features\n\n- **Stateful hot reload** that actually preserves running state. Edit the\n  program, click Hot Reload, keep going from where you were.\n- **Invoke** any zero-argument top-level function from an inline CodeLens\n  button above its declaration. The function runs on the live isolate.\n- **JavaScript interop** through one embedder native. Dart code can call\n  `alert`, `confirm`, `document.body`, `localStorage`, `Intl`, `canvas`,\n  anything in the host's JS runtime.\n- **Object Inspector** wired to the real VM Service Protocol. Walks the\n  live isolate's libraries, fields, and instances with lazy expansion.\n- **Sketch pane** with a `\u003ccanvas\u003e` that user code can draw into via the\n  JS interop.\n- **Expert mode** exposes per-function IL views, including a Graphviz\n  rendering of each compiler phase.\n- **Live Dart analyzer (LSP)**, the full analysis server `dart analyze`\n  uses locally, compiled to WebAssembly and exposed to the page over a\n  JSON-RPC bridge. Live diagnostics (errors, warnings, hints, lints),\n  completion (including auto-import from unimported libraries), and\n  hover docs -- the same IDE experience you get in VS Code or IntelliJ,\n  running entirely in your browser with no language-server backend.\n\n## Table of contents\n\n- [JavaScript interop](#javascript-interop)\n- [Inspector](#inspector)\n- [Sketch](#sketch)\n- [Expert mode](#expert-mode)\n- [Work in progress](#work-in-progress)\n- [Samples](#samples)\n- [How it works](#how-it-works)\n- [Bundle](#bundle)\n- [External packages](#external-packages)\n- [License](#license)\n\n## JavaScript interop\n\nUser code reaches the host's JS runtime through a single embedder native,\n`EmbedderJSEval`. One `external` declaration on the Dart side, one\n`Dart_SetNativeResolver` on the user's root library, one `EM_ASM_PTR`\ncall on the C++ side. Arguments cross the boundary as JSON.\n\n```dart\nimport 'dart:convert';\n\n@pragma(\"vm:external-name\", \"EmbedderJSEval\")\nexternal String _jsEvalRaw(String code, String argsJson);\n\nString jsEval(String code, [List\u003cObject?\u003e args = const []]) =\u003e\n    _jsEvalRaw(code, jsonEncode(args));\n\nvoid showAlert() {\n  jsEval('alert(a[0])', ['Hello from a Dart VM running in WebAssembly!']);\n}\n\nvoid formatWithIntl() {\n  print(jsEval(\n    'new Intl.NumberFormat(a[1], {style:\"currency\", currency:a[2]}).format(a[0])',\n    [1234567.89, 'de-DE', 'EUR'],\n  ));\n}\n```\n\nInside the JS snippet, `a` (also `args`) is the JSON-decoded argument\nlist. The result is whatever the JS expression evaluates to, stringified\n(`JSON.stringify` for objects, `String(...)` for primitives). A leading\n`!` on the returned string means JS threw. No SDK changes, no platform\nrebuild.\n\n## Inspector\n\nA toggleable side pane (toolbar `Inspect` button) wired to the **VM\nService Protocol**. The patched `runtime/vm/service.{h,cc}` exposes a new\npublic entry point, `Service::InvokeRpcSync`, which dispatches a method\non the current isolate without going through a service isolate, ports,\nor a WebSocket. The embedder forwards `getIsolate` / `getObject` calls\nthrough that entry point and the JS-side panel renders top-level fields\nwith lazy expansion for lists, maps, and plain instances. Uninitialized\nlazy fields show as `⟨NotInitialized⟩` (the protocol's Sentinel\nresponse). The inspector auto-refreshes after Run, Hot Reload, and every\nInvoke.\n\n## Sketch\n\nA second toggleable side pane (`Sketch` button) hosts a `\u003ccanvas\nid=\"sketch\"\u003e` element. User code draws into it via `jsEval`. A single\nbig eval per drawing means one C++ / JS hop for a whole scene; for\nanimation the loop lives in JS and Dart kicks it off with one call.\n\n## Expert mode\n\nThe toggle in the toolbar reveals two extra CodeLens buttons above every\ntop-level function:\n\n- `IL`, a text dump of the unoptimized flow graph from `Dart_CompileAll`.\n- `IL Graph`, the same flow graph rendered with `@viz-js/viz`, one\n  Graphviz `digraph` per compiler phase, with phase tabs across the top.\n\n## Work in progress\n\nThings that are sketched or partial and not yet wired into the live\ndeployment:\n\n- **Optimized IL extraction for cold functions.** Today optimized IL\n  shows only for functions the JIT has actually warmed up. A\n  `Compiler::CompileOptimizedFunction` helper can force optimization,\n  but linking it pulled in additional VM symbols that broke ReloadKernel\n  for kernels with added top-level functions; reverting to a stub kept\n  the trade-off favorable for now.\n- **Full `dart:js` / `dart:js_interop` backend.** The current interop\n  is a single string-based eval. Type-safe handles, real `JSObject`\n  references, and the standard `dart:js_interop` API would be a fourth\n  backend patch under `sdk/lib/_internal/vm/lib/` next to the existing\n  `dart2js` / `dart2wasm` / `ddc` ones, plus a platform rebuild.\n- **JS to Dart callbacks** without JSON marshalling. Per-frame\n  animation driven from Dart (instead of the JS-owned `setInterval`\n  loop the Sketch sample uses) needs a reverse callback path.\n- **pub.dev package search.** Pulling a Dart-only package off\n  pub.dev, resolving its deps client-side, and wiring it into the CFE\n  so user code can `import 'package:...'`.\n\n## Samples\n\nSix starred samples open with the headline features:\n\n- **Iterative π**, **Conway's Life**, **Bouncing particles** exercise\n  stateful hot reload plus invoke: initialize once, tweak the algorithm,\n  reload, keep going from where you were.\n- **Counter** is the smallest demo of the same idea.\n- **JavaScript interop (jsEval)** walks real browser APIs (alert,\n  confirm, prompt, DOM, localStorage, Intl).\n- **Canvas drawing (jsEval)** paints into the Sketch pane: a color\n  wheel, two Mandelbrots, a spirograph, a Dart-computed curve, and a\n  self-driving animation loop.\n\n## How it works\n\n1. You type Dart in Monaco.\n2. Browser JS calls `dartCompile(source, vm_platform.dill)`, and the CFE\n   wasm produces kernel bytes.\n3. JS hands those bytes to `dart_il_run`, which has the VM wasm execute\n   them through the ARM simulator.\n4. `print` calls in Dart go through an `EM_ASM` bridge back to JS, which\n   updates the DOM.\n5. **Run** orphans the prior isolate and creates a fresh one, so state\n   resets.\n6. **Hot Reload** calls `IsolateGroup::ReloadKernel` in place. Top-level\n   fields keep their values, but the library code is swapped to the new\n   kernel. After the reload `main()` re-runs so lazy initializers for\n   newly-added top-level fields fire.\n7. **Invoke** (the inline CodeLens above every zero-arg top-level function)\n   calls that function on the live isolate via `Dart_Invoke`, bypassing\n   entry-point pragmas via `--no-verify-entry-points`.\n8. `Future.delayed` actually waits wall-clock time. `_embedderSleep`\n   forwards to `emscripten_sleep` via Asyncify, so the JS event loop\n   stays responsive.\n9. The LSP server (`dart_lsp.wasm`) runs in parallel against\n   `dart_sdk.sum` + an optional `dart_packages.bin` external-package\n   source bundle. It speaks LSP JSON-RPC over a JS bridge\n   (`globalThis.lspSend` / `lspReceive`); the page wires this to Monaco's\n   completion provider, hover provider, and `setModelMarkers` for\n   diagnostics.\n\n## Bundle\n\nThe browser pulls these files; no server is involved:\n\n| component | wasm size | role |\n|---|---:|---|\n| `dart_il.wasm` | 9.7 MB | Dart VM (emcc, ARM simulator) |\n| `dart_cfe.wasm` | 2.4 MB | Common front-end (dart2wasm), Dart to kernel |\n| `dart_lsp.wasm` | 4.4 MB | Dart analysis server (dart2wasm). Speaks LSP over a JS bridge: diagnostics, completion, hover, auto-import. |\n| `vm_platform.dill` | 8.3 MB | Platform kernel (dart:core, dart:async, ...) |\n| `dart_sdk.sum` | 3.2 MB | Analyzer SDK summary |\n| `dart_packages.bin` | 0.9 MB | Default external-packages source bundle (`meta`, `collection`, `async`). Optional, see below. |\n\nTotal: about 29 MB uncompressed, ~8 MB gzipped.\n\n## External packages\n\nEmbedders ship two kinds of artifacts for external packages:\n\n- **Kernel dills** (one per top-level package), used by the VM at run time\n  for `import 'package:foo/...'` to actually execute.\n- **A single source bundle** (one DPKG blob covering every package and\n  its transitive deps), used by the analyzer for live diagnostics + the\n  upcoming completion / hover / goto-definition surface.\n\nDeclare them on `window` before the dart-live script tag loads:\n\n```html\n\u003cscript\u003e\n  window.dartLivePackages = [\n    { name: 'knex_dart', dillUrl: './knex_dart.dill' },\n  ];\n  window.dartLivePackagesBundle = './dart_packages.bin';\n\u003c/script\u003e\n```\n\nEither field is optional. With no `dartLivePackages`, `import 'package:...'`\nwon't execute. With no `dartLivePackagesBundle`, the page tries to fetch\n`./dart_packages.bin` next to `index.html` -- this repo ships a default\none covering `meta`, `collection`, and `async`, so those imports just\nwork out of the box. If neither the override nor the default bundle is\npresent, the analyzer will red-underline external imports but everything\nelse still works.\n\n### Build a `dartLivePackagesBundle` from pub.dev\n\nThe packer is a single node script in this repo:\n[`tools/pack_pub_packages.mjs`](tools/pack_pub_packages.mjs). It walks\nthe transitive dependency graph from pub.dev's HTTP API, downloads each\ntarball, extracts it in memory, and emits the DPKG blob in one shot:\n\n```sh\nnode tools/pack_pub_packages.mjs dart_packages.bin knex_dart\n```\n\nYou can list multiple seeds. For `knex_dart 1.2.0` the closure is 7\npackages (`knex_dart`, `logging`, `meta`, `collection`,\n`knex_dart_capabilities`, `universal_io`, `typed_data`) at ~1.4 MB.\n\nVersion selection: latest stable satisfying the parent's `^x.y.z`\nconstraint, first-resolved wins (no diamond reconciliation). Good\nenough for embedded playgrounds; for fully reproducible solves port\n`package:pub_semver` + a real solver.\n\n### Build a kernel dill on the host\n\nFor each top-level package you want to execute:\n\n```sh\ndart compile kernel \\\n    --platform=vm_platform.dill \\\n    -o knex_dart.dill \\\n    lib/knex_dart.dart\n```\n\nDrop `vm_platform.dill` next to your build (or fetch it from this repo).\nThe resulting dill resolves `package:knex_dart/knex_dart.dart` at runtime\nin the in-page VM.\n\n### Bundle format reference (DPKG)\n\nA single binary blob. Little-endian throughout:\n\n```\nbytes 0..3 : ascii \"DPKG\"\nu32        : nPackages\nfor each package:\n  u32      : nameLen, then nameLen utf-8 bytes (package name)\n  u32      : nFiles\n  for each file:\n    u32    : pathLen, then pathLen utf-8 bytes (relative path)\n    u32    : contentLen, then contentLen file bytes\n```\n\nFiles are mounted at `/ext_pkg_\u003ci\u003e/\u003crelPath\u003e` inside the wasm's\n`MemoryResourceProvider`. The analyzer builds a synthetic\n`package_config.json` listing every package, and a synthetic\n`user/pubspec.yaml` that declares every mounted package as a dependency\nof the user file (otherwise the analyzer's `_PubFilter` hides their\nsymbols from unimported-libraries completion).\n\n### What happens when a dependency is missing\n\nSource-based loading degrades gracefully: a missing package surfaces as\na localized \"Target of URI doesn't exist\" diagnostic on the offending\n`import` line, every identifier from that import becomes \"Undefined\nname\", and **the rest of the file analyzes normally**. Hover, completion,\nand goto-def on code that doesn't touch the missing import keep working.\nThis is the same UX you get in a real IDE when you forget to `pub get`.\n\nCompare to the old summary-based path, which threw\n`ArgumentError: Missing library: ...` from\n`LinkedElementFactory._reportMissingLibrary` and collapsed the entire\nanalysis request. That old path is gone -- `dart_analyzer.wasm` only\naccepts the DPKG bundle now.\n\n### Limitations\n\n- **Generated code.** Packages that rely on `build_runner` codegen\n  (`freezed`, `json_serializable`, `drift`, ...) only ship hand-written\n  source in their pub tarball; generated files are not in the bundle.\n  References to generated symbols flag as undefined.\n- **Bundle size.** Every `.dart` file in each package ships, plus\n  incidental files from the tarball (`README`, `CHANGELOG`, examples).\n  For a few-MB closure that's fine; for \"all of pub.dev\" you would\n  filter to `lib/**` in the packer.\n- **No private packages.** The packer only reads from pub.dev. For\n  local / git / forked packages, pack any directory tree into a DPKG\n  blob yourself -- the format is intentionally trivial.\n\n### Live example\n\nhttps://playground.knex.mahawarkartikey.in embeds dart-live with the\n`knex_dart` package preloaded both ways, so you can write\n`import 'package:knex_dart/knex_dart.dart';` and have it resolve,\ncompile, run, and type-check in the browser.\n\n## License\n\nThis repository contains a built artifact bundle. Build inputs and patches\nlive in a separate working tree (Dart SDK plus an emcc-built embedder).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodulovalue%2Fdart-live","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodulovalue%2Fdart-live","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodulovalue%2Fdart-live/lists"}