The Participatory System Mapper (PRSM) is a browser app that makes it easy for a group of people to draw networks (or “maps”) of systems together.
A map can be anything that has items (factors / nodes) connected by links (edges). Examples include:
- People connected by knowing each other
- Factors or variables linked by causal relationships
- Theories expressed as variables and relationships
- Company boards and shared directors
- Scientists co-authoring papers
- and similar network structures
PRSM is designed so groups can collaborate on the same map, each on their own computer or tablet. People may work face to face, remotely over video, or with the built-in chat. Every edit is shared live with other participants in the same room (similar to Google Docs for text).
When you start the app, a private room is created for your map. Only people with the room link can see and edit it.
- Home page: https://prsm.uk
- User guide: https://prsm.uk/help.html
- Source code: https://github.com/micrology/prsm
- For end users
- Running PRSM on your own network
- For developers: adapting and enhancing PRSM
- Licence
- Acknowledgements
- Contact
No installation is required. Open a modern browser and go to:
Supported browsers (current two major versions of each):
- Chrome
- Firefox
- Safari
- Edge
Internet Explorer is not supported.
The public service is free to use under a PolyForm Noncommercial License. See Licence for details.
If something goes wrong, or you have a suggestion, email nigel@prsm.uk.
Use this section if you want PRSM on an intranet, a private server, or a single machine that can work offline.
The easiest and most reliable way to self-host is the pre-built container images. Full end-user steps (including Windows / Mac / Linux notes) live in the user manual:
Running PRSM locally or on an intranet
(source: doc/help/docs/manual/Advanced/RunningLocally.mdx)
In short:
-
Install Python 3, Podman (or Docker), and
podman-compose/ Docker Compose. -
Use the compose file in this repo:
docker/compose.yaml. -
From the
docker/directory:podman machine init # first time only, if required podman machine start # if required podman-compose up -d
-
Open http://localhost:8080.
| Service | Image | Host port | Purpose |
|---|---|---|---|
| Static app + help | docker.io/micrology/prsm-httpd |
8080 | Web UI |
| Collaboration server | docker.io/micrology/prsm-y-websocket |
1234 | Live multi-user sync (Yjs) |
- Firewalls must allow 8080 and 1234 (or the ports you map in
compose.yaml). - Maps in the container are independent of maps on prsm.uk. You can still exchange maps via PRSM save/load files.
- By default, map data lasts only while the websocket container keeps its volume. For persistence across restarts, bind a host directory into the websocket service (see comments in
docker/compose.yamland the manual page above). - AI assistance and the HTTP map API are not available in this simple container setup; they need the separate API service and external configuration (see
api-server/).
To stop:
podman-compose downTo refresh images after a new release:
podman-compose down
podman-compose pull
podman-compose up -dEquivalent Docker Compose commands work the same way if you prefer Docker Engine instead of Podman.
Only choose this if containers are not an option. You will need to run and wire several pieces yourself:
| Piece | Directory | Role |
|---|---|---|
Frontend build (dist/) |
repo root | Static HTML/JS/CSS served by Apache, nginx, or similar |
| Websocket server | ws-server/ |
Collaboration + LevelDB persistence |
| API / AI proxy (optional) | api-server/ |
Programmatic map API and AWS Bedrock bridge |
| User manual build (optional) | doc/help/ |
Help site assets |
High-level production outline:
-
Install a current Node.js (20 LTS or newer is a practical minimum; help tooling expects
>=20.9.0. Container images currently build with Node 24). -
Clone the repo and install dependencies (see Developer setup).
-
Build the frontend (and help, if you serve it):
npm run deploy # or step-by-step: install:all → build-vis-network → build → build-help -
Serve the built files. Parcel output is in
dist/. The repo root.htaccessrewrites pretty URLs intodist/for Apache; enable at leastmod_headers(and typicallymod_rewrite/mod_deflateas used in that file).sudo a2enmod headers # Debian/Ubuntu sudo systemctl restart apache2 -
Run the websocket server with a persistent LevelDB directory (
YPERSISTENCE). Seews-server/(including the samplewebsocket-server.service). -
Optionally run the API server and configure secrets / env. See
api-server/(includingprsm-api-server.service). -
Put HTTPS and WebSocket upgrade (
wss) in front of the services with your reverse proxy. The public deployment uses paths such aswss://…/wssfor sync and/apifor the API.
Backup and restore of the LevelDB store are documented under ws-server/ (BACKUP-README.md and related scripts).
This section is for people changing the code, adding features, or packaging their own deployment.
Browser (html/ + js/ + css/)
│ Yjs over WebSocket
▼
ws-server ← live rooms, awareness, LevelDB persistence
▲
│ (optional) same Yjs rooms
api-server ← /api/map/…, /api/chat/…, help assistant, Bedrock
│
Static host ← Parcel build → dist/ (+ optional doc/help build)
Important libraries:
- yjs + y-websocket — multi-user CRDT sync
- vis-network — network rendering (vendored, modified fork in
vis-network/) - Parcel — frontend bundling into
dist/ - Supporting UI libs include Hammer, Quill, Fabric, Tabulator, Bootstrap, and others listed in root
package.json
AI features can be disabled at build/config time via "features": { "ai": … } in root package.json.
.
├── html/ # Browser entry pages (main map: prsm.html)
├── js/ # Frontend ES modules
├── css/ # Stylesheets
├── dist/ # Parcel build output (generated)
├── api-server/ # Map API + AWS Bedrock bridge → see api-server/README
├── ws-server/ # Yjs websocket server → see ws-server/README
├── vis-network/ # Modified vis-network fork → see vis-network/README
├── doc/ # Examples, JSDoc, user manual → see doc/README
│ ├── examples/
│ ├── help/ # Rspress user guide sources
│ └── jsdoc/
├── docker/ # Container images + compose.yaml
├── data/ # Sample / legacy maps
└── package.json # Root scripts and frontend dependencies
Frontend modules under js/ (high level):
| Module | Role |
|---|---|
prsm.js |
Main network pane and room lifecycle |
background.js |
Background drawing layer |
styles.js / samples.js |
Style editing and defaults |
files.js |
Import / export |
cluster.js |
Clustering |
table.js |
Data (table) view |
trophic.js |
Trophic layout |
betweenness.js |
Network statistics worker |
tutorial.js |
First-run tour |
projects.js |
Recent maps / projects menu |
utils.js |
Shared helpers |
merge.js |
Map merge / diff helpers |
ai.js |
LLM calls via the API server |
aiasst.js |
AI Help Assistant UI |
3d.js |
3D view |
new-index.js |
Landing / index helpers |
Details for each backend or subproject belong in that directory’s own README once published:
- git
- Node.js 20.9+ (Node 22+ or 24 recommended; match what you use in production if possible)
- npm (comes with Node)
- For full local AI: AWS credentials / Bedrock access as described in
api-server/ - For Apache-style deploys:
mod_headers(and related modules used by.htaccess)
git clone https://github.com/micrology/prsm.git
cd prsm
# Install root + subpackage dependencies
npm run install:all
# Optional: rebuild the vendored vis-network bundle after changing that fork
npm run build-vis-network
# Production-style frontend build
npm run buildOne-shot production build chain (deps + vis-network + app + help):
npm run deployThe root package can start the websocket server, API server (dev mode), and Parcel watch together:
npm run start:all-locallyThis runs approximately:
ws-serverwithYPERSISTENCE=./dbDiron port 1234api-serverwithNODE_ENV=devon port 3001 (talks tows://localhost:1234)parcel watchonhtml/*.html(no HMR)
Stop everything started that way:
npm run stop:all-locallyProduction frontend defaults:
- WebSocket:
wss://www.prsm.uk/wss - AI / help API: same origin (or
https://prsm.ukfor the help assistant)
For local servers, open the app with the debug=local flag, for example:
http://localhost:<parcel-or-http-port>/prsm.html?debug=local
With debug=local (or when the page is served from a non-standard HTTP port), the client uses:
- WebSocket
ws://<hostname>:1234 - AI chat API
http://localhost:3001/api/chat/…
The help assistant treats localhost / 127.0.0.1 as local and calls http://localhost:3001.
Other useful debug= tokens (comma-separated) are listed in the header comment in js/prsm.js (yjs, gui, prompt, plain, and others).
Root .htaccess CSP already allows ws://localhost:1234 and http://localhost:3001 for local connect targets when you serve via Apache with that file.
| Script | Purpose |
|---|---|
npm run install:all |
npm i in root, doc/help, api-server, vis-network, ws-server |
npm run upgrade:all |
Upgrade dependencies in those packages |
npm run dev |
Clean dist/ and Parcel-watch html/*.html |
npm run build |
Production Parcel build into dist/ |
npm run clean |
Remove dist/* |
npm run build-vis-network |
Build the vendored network library |
npm run build-help |
Production user-manual build |
npm run build-help-locally |
Local help build |
npm run deploy |
install:all + vis-network + app + help builds |
npm run start:all-locally |
ws-server + api-server (dev) + Parcel watch |
npm run stop:all-locally |
Stop the local stack processes |
npm run lint |
ESLint with --fix on js/*.js |
npm run pretty |
Prettier on HTML/JS/CSS and related paths |
npm run spellcheck |
cspell on main sources and help MDX |
npm run check |
lint + spellcheck + help build + prettier |
npm run jsdoc |
Generate JSDoc under doc/jsdoc |
npm run analyse |
Parcel detailed bundle report |
npm run rebuild-docker |
Multi-arch build/push of published images |
Subpackages define additional scripts; see their READMEs and package.json files.
- Vanilla JS (ES2021+), HTML5, modern CSS — no SPA framework
camelCasefor variables/functions,PascalCasefor classes,UPPER_SNAKE_CASEfor constants- Prefer CSS classes over inline styles
- Document public functions with JSDoc
- Format / lint with the root scripts above (
pretty,lint) - Keep changes aligned with existing file patterns; prefer simple, single-purpose functions
Browser targets are the browserslist in root package.json (last two versions of Chrome, Firefox, Safari, Edge).
There is no automated test suite at present.
| Concern | Where |
|---|---|
| Enable / disable AI UI | package.json → features.ai |
| Default production WebSocket URL | js/prsm.js (websocket default) |
| Local WebSocket / API selection | URL debug=local; see js/prsm.js, js/ai.js, js/aiasst.js |
| API port, Bedrock, CORS, secrets | api-server/ (env + Secrets Manager) |
| Websocket host/port/persistence | ws-server/ (HOST, PORT, YPERSISTENCE, VERBOSE) |
| URL rewriting / CSP | .htaccess |
| Container packaging | docker/ |
Dockerfiles and comments live under docker/. Root script:
npm run rebuild-dockerbuilds and pushes multi-arch micrology/prsm-y-websocket and micrology/prsm-httpd images. Prefer the published images for ordinary self-hosting (section 2).
PRSM is Copyright (c) 2022– Nigel Gilbert (prsm@prsm.uk).
It is available under the PolyForm Noncommercial License 1.0.0. Full text and notes are in License.md.
For other uses (for example commercial or internal proprietary deployment under a different licence), contact the licensor at prsm@prsm.uk.
Thanks to everyone who inspired, suggested, reviewed, and tested PRSM, including members of CECAN, CRESS, Risk Solutions, and Robin Gilbert.
Bug reports, feature requests, and other feedback: nigel@prsm.uk.