# pflow.xyz — Petri net editor, simulator, and browser math library pflow.xyz is a visual Petri net editor with an embeddable, dependency-free ES module library for discrete simulation and continuous (ODE) analysis. All modules are served from this origin with `Access-Control-Allow-Origin: *`, so they can be imported directly from any ad-hoc HTML page — no build step, no npm. ## Modules (import these URLs directly) - https://pflow.xyz/petri-solver.js — mass-action ODE solver (Tsit5, adaptive) plus SVGPlotter, an interactive SVG line/scatter plotter. Exports: fromJSON, setState, setRates, ODEProblem, ODESolution, Tsit5, solve, SVGPlotter, Place, Transition, Arc, PetriNet. - https://pflow.xyz/petri-sim.js — discrete firing semantics (pure functions). Exports: enabled, fire, enabledTransitions, marking, getArcWeight, capacityOf. - https://pflow.xyz/petri-colors.js — colored-net unfolding (expandColors, expandState). petri-solver.js imports it automatically. - https://pflow.xyz/petri-view.js — the full `` editor web component (drag-and-drop editing, discrete play mode, tabbed Analysis workbench: ODE simulate / rate scan / sweep / phase plot). - https://pflow.xyz/petri-view.css — editor styles (design tokens, light + dark via prefers-color-scheme). - https://pflow.xyz/petri-learn.js — differentiable fitting for Petri-net ODE models: a JS mirror of go-pflow's `learn` package. Forward sensitivities (`LearnableProblem.solveWithSensitivities`, augmented ODE with analytic Jacobian) and reverse-mode/adjoint sensitivities (`LearnableProblem.solveAdjoint`, one backward solve — the tool for a rate function with many parameters, at a cost independent of parameter count). Exports: `LearnableProblem`, `ScalarRateFunc`, `SharedScalar` (tied parameters), `mseLossGrad`/`relativeMseLossGrad`/`mseLossAdjoint`/ `relativeMseLossAdjoint`, `adamMinimize`/`nelderMead`/`coordinateDescent`, `hingeRankLoss`, `fit`/`fitGradient`/`fitRates` (the no-boilerplate entry point: fit per-transition scalar rates to observed trajectories). petri-solver.js's bytes are untouched by this module. - https://pflow.xyz/petri-ssa.js — portable Gillespie SSA (discrete-stochastic simulation), byte-exact with go-pflow, pflow-rs and pflow-jl via a fixed PRNG (SplitMix64 → xoshiro256**) and a ported log; no dependency on the other modules. `simulate(model, {horizon, samples, realizations, seed})` → `{ places, times, series: {id: {values, stddev}}, final }` (ensemble mean/stddev/final per place). Exports: splitmix64, Xoshiro256, plog, combinations, compile, simulate. - https://pflow.xyz/petri-sde.js — chemical Langevin SDE, the third leg of Petri.jl's ODEProblem/JumpProblem/SDEProblem trio: continuous state via Euler-Maruyama, but with the net's own intrinsic firing noise rather than SSA's discrete events or the ODE's none at all. Reuses petri-ssa.js's compile(), Xoshiro256 and plog rather than duplicating them. `simulateSDE(model, {horizon, samples, realizations, seed})` → the same shape as simulate(), plus `diverged`/`reason`/`caveats` when the model gates (read arc, inhibitor, reached capacity — none has a continuous analogue). Exports: GaussianSampler, combinationsReal, simulateSDE, CHEMICAL_LANGEVIN_ASSUMPTION. Not yet part of the byte-exact cross-language contract SSA has — its Gaussian sampler is checked bit-for-bit against go-pflow's own reference vectors, but the ensemble output is checked only for consistency against this module's own SSA. Which one for which question: petri-solver.js's ODE has no firing instant, so it cannot express a read arc, an inhibitor, a reached capacity or a guard — the go-pflow reference implementation refuses a model that has any of these rather than silently ignore them, and this module's semantics match it. petri-sde.js refuses the same way, for the same reason. Use petri-ssa.js for a gated model, or whenever token counts are small enough that variance is the answer rather than noise around it; use petri-sde.js when you want that noise without individual discrete events (large populations, continuous-state tooling that still needs the fluctuation). Above input-arc weight 1 all three rate laws diverge genuinely (mass action multiplies each input's concentration in once regardless of weight; SSA's and the SDE's propensity use the full combinatorial C(m, weight) term, continuous in the SDE's case) — not a bug in any of them, a different question. Full rules with a worked example: docs/engine-selection.md (vendored from go-pflow, scripts/docs-sync.sh check/sync). The ODE semantics are behaviorally identical to go-pflow (github.com/pflow-xyz/go-pflow) — enforced by a differential parity test suite — so browser results match server-side analysis. ## Model format (JSON-LD) ```json { "@context": "https://pflow.xyz/schema", "@type": "PetriNet", "@version": "1.1", "token": ["https://pflow.xyz/tokens/black"], "places": { "water": { "@type": "Place", "initial": [10], "capacity": [0], "x": 60, "y": 60 }, "coffee": { "@type": "Place", "initial": [0], "capacity": [0], "x": 260, "y": 60 } }, "transitions": { "brew": { "@type": "Transition", "x": 160, "y": 60 } }, "arcs": [ { "@type": "Arrow", "source": "water", "target": "brew", "weight": [1] }, { "@type": "Arrow", "source": "brew", "target": "coffee", "weight": [1] } ] } ``` Notes: `capacity` 0 or absent means unbounded. `initial`, `capacity`, and `weight` are vectors — one entry per token color in `token`; an explicit 0 weight component means "this color is not involved". Set `"inhibitTransition": true` on an arc for an inhibitor. ## Sandboxed environments (strict-CSP hosts) **claude.ai artifacts cannot load these modules today.** Artifact CSP allows external scripts only from `cdnjs.cloudflare.com`; imports from pflow.xyz, jsDelivr, esm.sh, or unpkg are all refused there and fail with `Failed to fetch dynamically imported module`. Don't write a fallback loader for artifacts — it fails on every origin. Host the page elsewhere (any static host works; the modules are CORS-open). A cdnjs submission is staged; once accepted, `https://cdnjs.cloudflare.com/ajax/libs/pflow-xyz//petri-solver.js` will be the artifact-compatible URL — until then that URL 404s. Also: `file://` pages can't load ES modules at all (CORS blocks them), so "open the HTML file in a browser" is not enough for local use — serve it, e.g. `python3 -m http.server`. For *other* strict-CSP hosts that do allow common CDNs, jsDelivr mirrors the repo; relative imports (petri-colors.js) resolve against whichever origin loaded the module, so either source is self-sufficient: ```js const pf = await import("https://pflow.xyz/petri-solver.js") .catch(() => import("https://cdn.jsdelivr.net/gh/pflow-xyz/pflow-xyz@v1.27.0/public/petri-solver.js")); ``` Pin a release tag (as above) or commit SHA — `@main` is cached ~12h by jsDelivr, so it lags the latest deploy. `@pflow-xyz/pflow-xyz` has been on npm since 2026-08-01, so npm-backed CDNs work too: `https://esm.sh/@pflow-xyz/pflow-xyz@1.27.0/petri-solver.js` or `https://unpkg.com/@pflow-xyz/pflow-xyz@1.27.0/petri-solver.js` (same paths work for petri-learn.js, petri-sim.js, etc. — see package.json's `exports` map). cdnjs listing is pending (needs 800+ npm downloads/month or 200 GitHub stars). ## Minimal ad-hoc page: solve an ODE and plot it ```html
``` The emitted SVG uses `var(--pv-viz-*, lightFallback)` colors: it renders the validated light palette standalone, and follows the host theme if the page defines those custom properties (petri-view.css does). ## Minimal ad-hoc page: full editor ```html ``` ## Discrete simulation ```js import * as sim from "https://pflow.xyz/petri-sim.js"; sim.enabledTransitions(model); // -> ["brew"] const marks = sim.fire(model, "brew"); // -> {"water":[9],"coffee":[1]} or null ``` `fire` is pure: pass the returned marking back as the third argument to step again. ## Related services - POST https://pflow.xyz/api/svg — render a model to SVG server-side. - GET https://pflow.xyz/schema — the JSON-LD @context. - https://sim.pflow.xyz/mcp — sim MCP server (JSON-RPC): build, run and check models (sim_create_model, sim_scenario, sim_invariants, sim_reach, sim_verify, …). - https://book.pflow.xyz — the book on modeling with Petri nets. - https://sim.pflow.xyz — describe a process, get a model you can run and check: store a model with one POST, then seeded scenario comparison, invariants and reachability proofs, calibration against event logs, and diagnostics that rank every knob by measured influence on the objective. MCP server at `https://sim.pflow.xyz/mcp`. - https://github.com/pflow-xyz/pflow-native — native desktop editor and simulator for the same JSON-LD models (pflow-rs), with a CLI and an MCP server (`pflow-native-cli mcp`).