Pathview
A Python first graphical user interface for PathSim.
Install / Use
/learn @pathsim/PathviewREADME
PathView - System Modeling in the Browser
A web-based visual node editor for building and simulating dynamic systems with PathSim as the backend. Runs entirely in the browser via Pyodide by default — no server required. Optionally, a Flask backend enables server-side Python execution with any packages (including those with native dependencies that Pyodide can't run). The UI is hosted at view.pathsim.org, free to use for everyone.
Tech Stack
- SvelteKit 5 with Svelte 5 runes
- SvelteFlow for the node editor
- Pyodide for in-browser Python/NumPy/SciPy
- Plotly.js for interactive plots
- CodeMirror 6 for code editing
Installation
pip install (recommended for users)
pip install pathview
pathview serve
This starts the PathView server with a local Python backend and opens your browser. No Node.js required.
Options:
--port PORT— server port (default: 5000)--host HOST— bind address (default: 127.0.0.1)--no-browser— don't auto-open the browser--debug— debug mode with auto-reload
Convert .pvm to Python
Convert PathView model files to standalone PathSim scripts:
pathview convert model.pvm # outputs model.py
pathview convert model.pvm -o output.py # custom output path
pathview convert model.pvm --stdout # print to stdout
Or use the Python API directly:
from pathview import convert
python_code = convert("model.pvm")
Development setup
npm install
npm run dev
To use the Flask backend during development:
pip install flask flask-cors
npm run server # Start Flask backend on port 5000
npm run dev # Start Vite dev server (separate terminal)
# Open http://localhost:5173/?backend=flask
Project Structure
src/
├── lib/
│ ├── actions/ # Svelte actions (paramInput)
│ ├── animation/ # Graph loading animations
│ ├── components/ # UI components
│ │ ├── canvas/ # Flow editor utilities (connection, transforms)
│ │ ├── dialogs/ # Modal dialogs
│ │ │ └── shared/ # Shared dialog components (ColorPicker, etc.)
│ │ ├── edges/ # SvelteFlow edge components (ArrowEdge)
│ │ ├── icons/ # Icon component (Icon.svelte)
│ │ ├── nodes/ # Node components (BaseNode, EventNode, AnnotationNode, PlotPreview)
│ │ └── panels/ # Side panels (Simulation, NodeLibrary, CodeEditor, Plot, Console, Events)
│ ├── constants/ # Centralized constants (nodeTypes, layout, handles)
│ ├── events/ # Event system
│ │ └── generated/ # Auto-generated from PathSim
│ ├── export/ # Export utilities
│ │ └── svg/ # SVG graph export (renderer, types)
│ ├── nodes/ # Node type system
│ │ ├── generated/ # Auto-generated from PathSim
│ │ └── shapes/ # Node shape definitions
│ ├── plotting/ # Plot system
│ │ ├── core/ # Constants, types, utilities
│ │ ├── processing/ # Data processing, render queue
│ │ └── renderers/ # Plotly and SVG renderers
│ ├── routing/ # Orthogonal wire routing (A* pathfinding)
│ ├── pyodide/ # Python runtime (backend, bridge)
│ │ └── backend/ # Modular backend system (registry, state, types)
│ │ ├── pyodide/ # Pyodide Web Worker implementation
│ │ └── flask/ # Flask HTTP/SSE backend implementation
│ ├── schema/ # File I/O (save/load, component export)
│ ├── simulation/ # Simulation metadata
│ │ └── generated/ # Auto-generated defaults
│ ├── stores/ # Svelte stores (state management)
│ │ └── graph/ # Graph state with subsystem navigation
│ ├── types/ # TypeScript type definitions
│ └── utils/ # Utilities (colors, download, csvExport, codemirror)
├── routes/ # SvelteKit pages
└── app.css # Global styles with CSS variables
pathview/ # Python package (pip install pathview)
├── app.py # Flask server (subprocess management, HTTP routes)
├── worker.py # REPL worker subprocess (Python execution)
├── cli.py # CLI entry point (pathview serve)
├── converter.py # PVM to Python converter (public API)
├── data/ # Bundled data files
│ └── registry.json # Block/event registry for converter
└── static/ # Bundled frontend (generated at build time)
scripts/
├── config/ # Configuration files for extraction
│ ├── schemas/ # JSON schemas for validation
│ ├── pathsim/ # Core PathSim blocks, events, simulation config
│ ├── pathsim-chem/ # Chemical toolbox blocks
│ ├── pyodide.json # Pyodide version and preload packages
│ ├── requirements-pyodide.txt # Runtime Python packages
│ └── requirements-build.txt # Build-time Python packages
├── generated/ # Generated files (from extract.py)
│ └── registry.json # Block/event registry with import paths
├── extract.py # Unified extraction script
└── pvm2py.py # Standalone .pvm to Python converter
Architecture Overview
Data Flow
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Graph Store │────>│ pathsimRunner │────>│ Python Code │
│ (nodes, edges) │ │ (code gen) │ │ (string) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
v
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Plot/Console │<────│ bridge.ts │<────│ Backend │
│ (results) │ │ (queue + rAF) │ │ (Pyodide/Flask) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Streaming Architecture
Simulations run in streaming mode for real-time visualization. The worker runs autonomously and pushes results without waiting for the UI:
Worker (10 Hz) Main Thread UI (10 Hz)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Python loop │ ────────> │ Result Queue │ ────────> │ Plotly │
│ (autonomous) │ stream- │ (accumulate) │ rAF │ extendTraces │
│ │ data │ │ batched │ │
└──────────────┘ └──────────────┘ └──────────────┘
- Decoupled rates: Python generates data at 10 Hz, UI renders at 10 Hz max
- Queue-based: Results accumulate in queue, merged on each UI frame
- Non-blocking: Simulation never waits for plot rendering
- extendTraces: Scope plots append data incrementally instead of full re-render
Wire Routing
PathView uses Simulink-style orthogonal wire routing with A* pathfinding:
- Automatic routing: Wires route around nodes with 90° bends only
- User waypoints: Press
\on selected edge to add manual waypoints - Draggable waypoints: Drag waypoint markers to reposition, double-click to delete
- Segment dragging: Drag segment midpoints to create new waypoints
- Incremental updates: Spatial indexing (O(1) node updates) for smooth dragging
- Hybrid routing: Routes through user waypoints: Source → A* → W1 → A* → Target
Key files: src/lib/routing/ (pathfinder, grid builder, route calculator)
Key Abstractions
| Layer | Purpose | Key Files |
|-------|---------|-----------|
| Main App | Orchestrates panels, shortcuts, file ops | routes/+page.svelte |
| Flow Canvas | SvelteFlow wrapper, node/edge sync | components/FlowCanvas.svelte |
| Flow Updater | View control, animation triggers | components/FlowUpdater.svelte |
| Context Menus | Right-click menus for nodes/canvas/plots | components/ContextMenu.svelte, contextMenuBuilders.ts |
| Graph Store | Node/edge state, subsystem navigation | stores/graph/ |
| View Actions | Fit view, zoom, pan controls | stores/viewActions.ts, stores/viewTriggers.ts |
| Clipboard | Copy/paste/duplicate operations | stores/clipboard.ts |
| Plot Settings | Per-trace and per-block plot options | stores/plotSettings.ts |
| Node Registry | Block type definitions, parameters | nodes/registry.ts |
| Code Generation | Graph → Python code | pyodide/pathsimRunner.ts |
| Backend | Modular Python execution interface | pyodide/backend/ |
| Backend Registry | Factory for swappable backends | pyodide/backend/registry.ts |
| PyodideBackend | Web Worker Pyodide implementation | pyodide/backend/pyodide/ |
| FlaskBackend | HTTP/SSE Flask server implementation | pyodide/backend/flask/ |
| Simulation Bridge | High-level simulation API | pyodide/bridge.ts |
| Schema | File/component save/load operations | schema/fileOps.ts, schema/componentOps.ts |
| Export Utils | SVG/CSV/Python file downloads | utils/download.ts, export/svg/, utils/csvExport.ts |
Centralized Constants
Use these imports instead of magic strings:
import { NODE_TYPES } from '$lib/constants/nodeTypes';
// NODE_TYPES.SUBSYSTEM, NODE_TYPES.INTERFACE
import { PORT_COLORS, DIALOG_COLOR_PALETTE } from '$lib/utils/colors';
// PORT_COLORS.default, etc.
Adding New Blocks
Blocks are extracted automatically from PathSim using the Block.info() classmethod. The extraction is config-driven for easy maintenance.
1. Ensure the block exists in PathSim
The block must be importabl
