Skip to content
Development
Skill

/wasm-compatibility

Check if a marimo notebook is compatible with WebAssembly (WASM) and report any issues.

From plugin
skills
17710 skills

Claude Code

$ npx -y skills add marimo-team/skills --skill wasm-compatibility --agent claude-code

SKILL.md

wasm-compatibility.SKILL.md
name: wasm-compatibility
description: Check if a marimo notebook is compatible with WebAssembly (WASM) and report any issues.

WASM Compatibility Checker for marimo Notebooks

Check whether a marimo notebook can run in a WebAssembly (WASM) environment — the marimo playground, community cloud, or exported WASM HTML.

Instructions

1. Read the notebook

Read the target notebook file. If the user doesn't specify one, ask which notebook to check.

2. Run marimo's WASM lint rules

marimo ships three lint rules that catch most WASM incompatibilities automatically. Run them first, and build the report from their output instead of re-deriving these checks by hand:

marimo check <notebook> --select MW --format json
  • **MW001** `incompatible-import` — stdlib modules missing or non-functional in Pyodide (`subprocess`, `pdb`, `dbm`, `resource`, `fcntl`, `termios`, `readline`, `curses`, `tkinter`, `pydecimal`, `test`), plus multiprocessing exports and submodules that need native synchronization, shared memory, pipes, or process forking (`Lock`, `Condition`, `Semaphore`, `Barrier`, `Manager`, `Pipe`, `RLock`, `shared_memory`, `ForkContext`/`ForkProcess`, `ThreadPool`, and others). `Process`, `Queue`, `SimpleQueue`, `Pool`, and `ProcessPoolExecutor` stay allowed — they run on WASM-compatible cooperative adapters (see step 3).
  • **MW002** `unsafe-system-call` — calls such as `os.system()`, `os.fork()`, `signal.signal()`, `breakpoint()` that fail in Pyodide even when the import itself succeeds.
  • **MW003** `incompatible-package` — resolves the notebook's PEP 723 dependency tree (skipping anything excluded by a `sys_platform != 'emscripten'` marker) and checks PyPI for a WASM-compatible wheel, catching cases like `jaxlib` pulled in transitively through `jax`.

**Caveat:** MW001 flags import statements (`from multiprocessing import Lock`, `import multiprocessing.synchronize`), not later attribute access. A bare `import multiprocessing` followed by `multiprocessing.Lock()` deeper in the code can slip through. For notebooks with concurrency code, skim for this by hand too.

**Caveat:** a clean MW003 result does not by itself mean a package is safe to use in the browser. If a notebook marks a package `sys_platform != 'emscripten'`, MW003 skips it entirely — that only tells you the package will not be *installed* in WASM, not that the notebook is free to use it. Check whether the notebook also has `cache_cells = true` and was exported with `--execute` (see step 4). If so, the excluded package can still be used, through caching. If not, any code path that imports or touches the excluded package will fail in the browser, and MW003 will not have warned about it.

3. Check what the linter does not cover

These stay manual, agent-driven checks:

  • **PEP 723 metadata.** If the notebook has no `# /// script` block, or imports a package that is not listed in `dependencies`, WARN. Without that metadata, packages do not auto-install when the notebook starts in WASM. Version pins and lower bounds are fine — marimo strips version constraints when running in WASM.
  • **Runtime-only patterns.** Static lint does not catch these: reading environment variables (`os.environ`, `os.getenv`), hard-coded absolute file paths or `Path.home()`/`Path.cwd()` expecting a real filesystem, and large in-memory datasets (WASM notebooks are capped at 2GB). Flag these where you see them.
  • **Concurrency semantics.** WASM notebooks run cooperative adapters for `threading.Thread`, `Event`, `local`, `ThreadPoolExecutor`, `wait`, `as_completed`, and process-shaped `multiprocessing.Process`, `Queue`, `SimpleQueue`, `Pool`, `ProcessPoolExecutor`. The API shape is real, but there are no OS threads, no shared memory, and no true parallelism. Everything runs one task at a time in the single Pyodide interpreter. If the notebook relies on actual concurrent speedup rather than the API shape alone, warn that it runs correctly but sequentially.
  • **Cached execution already in place.** Check the notebook's `pyproject.toml` (or inline `# /// script` block) for `[tool.marimo.runtime] cache_cells = true`. If it is set, an incompatible package marked `sys_platform != 'emscripten'` is not automatically a FAIL — see the escape hatches in step 4.

4. Produce the report

Output a clear, actionable report:

**Compatibility: PASS / FAIL / WARN**

  • **PASS** — the lint check and step 3 both found nothing.
  • **WARN** — nothing failed, but step 3 turned up something worth a second look (missing PEP 723 metadata, a runtime-only pattern, or concurrency that will run but not in parallel).
  • **FAIL** — the lint check reported a diagnostic, or step 3 found something that will not run at all.

**Lint findings** — paste the diagnostics from step 2 verbatim (file, line, code, message).

**Manual findings** — list anything from step 3, with the cell or line and a suggested fix.

**Recommendations** — for FAIL/WARN notebooks, suggest concrete fixes:

  • Replace an incompatible package with a WASM-friendly alternative
  • Rewrite an incompatible code pattern

For a package with no WASM build, there is no drop-in replacement. Two escape hatches exist, and they solve different problems — check which one the notebook actually needs:

1. **Precompute every reachable result and cache it.** Exclude the package from WASM with a `sys_platform != 'emscripten'` marker. marimo then runs the real computation once, server-side, and bundles the result into the export:

   # /// script
   # dependencies = [
   #     "marimo",
   #     "torch; sys_platform != 'emscripten'",
   # ]
   #
   # [tool.marimo.runtime]
   # cache_cells = true
   # ///
   marimo export html-wasm notebook.py -o output_dir --execute

This covers a single static result (a plot, a one-off computation) directly. For a small, enumerable set of results — a dropdown or slider over a fixed list of options — decorate the computation with `@mo.persistent_ca

Read more
@marimo-team
Ships withskills

This repository contains skills for use with marimo. Install these skills with: The benefit of npx skills is that it supports many agents. So you can do things like: We welcome feedback: issues and pull requests are welcome.

Stars on GitHub

Momentum

stars in total
177
Star history of marimo-team/skills on GitHub, a chart by star-history.com

Other skills on skills.

add-molab-badge

add-molab-badge

Add "Open in molab" badge(s) linking to marimo notebooks. Works with READMEs, docs, websites, or any markdown/HTML target.

@marimo-team@marimo-teamInstall Skill
implement-paper

implement-paper

Implement a research paper as an interactive marimo notebook together with the user. Start by understanding what the user wants…

@marimo-team@marimo-teamInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about wasm-compatibility.

Which coding agents can use wasm-compatibility?
wasm-compatibility installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.