GSoC Report for DoodleBUGS: a Browser-Based Graphical Interface for Drawing Probabilistic Graphical Models
GSoC
Blog
Shravan Goswami’s GSoC 2025 final report: goals, architecture, progress vs proposal, and how to try it.
TL;DR
- BUGS (Bayesian Inference Using Gibbs Sampling) is a probabilistic programming language for Bayesian models, and JuliaBUGS is its modern implementation in Julia. DoodleBUGS is a browser-based graphical interface for JuliaBUGS, allowing users to draw probabilistic graphical models and generate BUGS code.
- Implemented: visual editor (nodes, edges, nested plates), legacy BUGS code generation that compiles with JuliaBUGS [1], [2], local execution via a Julia backend, unified standalone script generation (frontend), timeouts, multiple layouts, and extensive cleanup/typing.
- Changed from proposal: frontend implemented in Vue 3 (instead of React); backend simplified (frontend is the single source of truth for standalone scripts).
- Status: Working application. Try it here (static UI): https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/. For local inference, run the backend server.
DoodleBUGS UI
DoodleBUGS Project Structure
Directory Structure
DoodleBUGS/ # Vite + Vue 3 app (UI editor)
├── README.md # project documentation
├── public/ # static assets served by Vite
│ └── examples/ # example projects
├── experiments/ # prototypes and exploratory work
├── runtime/ # Julia HTTP backend (API endpoints & dependencies)
├── src/ # application source
│ ├── assets/ # styles and static assets
│ ├── components/ # Vue components composing the UI
│ │ ├── canvas/ # graph canvas and toolbars
│ │ ├── common/ # shared UI primitives
│ │ ├── layouts/ # app layout and modals
│ │ │ └── MainLayout.vue # main application layout
│ │ ├── left-sidebar/ # palette, project manager, execution settings
│ │ ├── panels/ # code preview and data input panels
│ │ ├── right-sidebar/ # execution, JSON editor, node properties
│ │ └── ui/ # base UI elements (buttons, inputs, selects)
│ ├── composables/ # reusable logic (codegen, drag & drop, graph, validator, grid)
│ ├── config/ # configuration and node definitions
│ ├── stores/ # Pinia state stores (graph, data, execution, project, UI)
│ └── types/ # TypeScript types and ambient declarations
├── tmp/ # local temporary outputs (ignored in builds)
└── ztest/ # scratch/test artifacts
Motivation
JuliaBUGS is a modern Julia implementation of the BUGS language [2], [3], [4]. DoodleBUGS revives the original visual modelling concept with a modern browser-based stack so users can:
- Construct probabilistic graphical models visually (nodes, edges, plates).
- Export readable legacy BUGS code that compiles with JuliaBUGS [1], [2], [3].
- Run inference and inspect results from the UI. Common BUGS applications include parallel MCMC [5], survival analysis [6], and Gibbs-style samplers [7], [8].
What Was Built
- Visual editor
- Node types: stochastic, observed, deterministic
- Plates with arbitrary nesting; robust drag-in/out and creation inside plates
- Graph layouts: Dagre (default), fCoSE (Force-Directed), Cola (Physics Simulation), KLay (Layered); stable interactions
- Legacy BUGS code generation [2], [3]
- Topological ordering and plate-aware traversal
- Parameter formatting and safe index expansion
- Implemented in
DoodleBUGS/src/composables/useBugsCodeGenerator.ts
- Execution flow
- Frontend POSTs to
/api/run
with body:model_code
(BUGS),data
andinits
(JSON),data_string
andinits_string
(Julia NamedTuple literals), andsettings
{ n_samples, n_adapts, n_chains, seed, timeout_s }
. If/api/run
returns 404, it falls back to/api/run_model
. - Backend creates a temp dir, writes
model.bugs
andpayload.json
, generates an ephemeralrun_script.jl
, compiles withJuliaBUGS.@bugs
, wraps withADgradient(:ReverseDiff)
, and samples viaAdvancedHMC.NUTS
throughAbstractMCMC
(Threads or Serial). It writes summaries (incl. ESS, R-hat) and quantiles to JSON and returns{ success, summary, quantiles, logs, files[] }
, wherefiles
includesmodel.bugs
,payload.json
,run_script.jl
, andresults.json
. - Frontend also generates a
standalone.jl
script locally (mirrors backend execution) and shows it alongside the backend files; the backend does not attach a standalone script.
- Frontend POSTs to
- Timeouts/resilience
- Configurable timeout (frontend); enforced in backend worker
- Safe temp directory cleanup on Windows with retries
- Cleanup/typing
- Strong, project-wide TypeScript typing across stores, components, and composables
- Removal of unused backend code; consistent naming and logs
Architecture Overview
- Frontend: Vue 3, Pinia, Cytoscape.js [9], CodeMirror
- Code generation:
DoodleBUGS/src/composables/useBugsCodeGenerator.ts
- Execution panel:
DoodleBUGS/src/components/right-sidebar/ExecutionPanel.vue
- Code generation:
- Backend (Julia) HTTP server
- Server:
DoodleBUGS/runtime/server.jl
- Project deps:
DoodleBUGS/runtime/Project.toml
(HTTP, JSON3, JuliaBUGS, AbstractMCMC, AdvancedHMC, ReverseDiff, MCMCChains, DataFrames, StatsBase, Statistics) - Endpoints: GET
/api/health
; POST/api/run
and/api/run_model
- Execution: creates temp dir, writes
model.bugs
andpayload.json
, generatesrun_script.jl
, enforces optional timeout
- Server:
Design Principles and Architecture
Design principles
- Visual-first modeling with deterministic export to legacy BUGS [2], [3].
- Separation of concerns: editing (graph), generation (BUGS), execution (backend), and results (summary/quantiles) are modular.
- Deterministic ordering: topological sort + plate-aware traversal ensures readable, stable code output.
- Robustness: cancellable frontend fetch, backend-enforced timeout, and resilient temp cleanup on Windows (
safe_rmdir()
).
Frontend architecture (Vue 3 + Cytoscape.js)
- Core graph state is managed in Vue; Cytoscape.js handles layout, hit-testing, and interaction semantics (including compound nodes for plates) [9].
- Code generation lives in
DoodleBUGS/src/composables/useBugsCodeGenerator.ts
and mapsGraphNode
/GraphEdge
to BUGS:- Kahn topological sort for definition order
- Plate-aware recursion for
for (...) { ... }
blocks - Parameter canonicalization (indices, numeric/expr passthrough)
- Standalone Julia script generation uses
generateStandaloneScript()
in the same composable, mirroring backend execution.
Backend architecture (Julia)
run_model_handler()
inDoodleBUGS/runtime/server.jl
materializesmodel.bugs
,payload.json
, and a transientrun_script.jl
that:- Builds
NamedTuple
s from JSON or string-literal data/inits - Compiles via
JuliaBUGS.@bugs
, wraps withADgradient(:ReverseDiff)
[10] - Samples with
AdvancedHMC.NUTS
throughAbstractMCMC
(Threads or Serial) [11], [12], [13] - Emits summaries (incl. ESS and R-hat) via
MCMCChains
/DataFrames
and quantiles to JSON [14], [15] - Timeout: worker process is killed if exceeding
timeout_s
. - Cleanup:
safe_rmdir()
retries with GC to avoid EBUSY on Windows.
- Builds
Why Vue (not React)?
The proposal planned React; we chose Vue 3 after evaluating the graph layer and developer velocity for this app.
- Tried Konva (canvas) for custom graph editing: powerful drawing primitives, but required bespoke graph semantics (hit testing, edge routing, compound nodes) that Cytoscape.js provides out of the box.
- Tried D3 force/layouts: flexible, but compound nodes (plates), nesting, and drag constraints became a significant amount of custom code to maintain.
- Cytoscape.js offered:
- Vue 3 (vs React) for this project:
- Composition API made integrating an imperative graph library (Cytoscape) straightforward via composables and lifecycle hooks
- SFC ergonomics and Pinia stores enabled quick iteration with strong TypeScript support
- Template reactivity + refs reduced reconciliation overhead when bridging to Cytoscape’s imperative API
- Minimal glue code for state management (Pinia) vs setting up reducers/selectors; enabled rapid iteration
- Vite + Vue tooling yielded fast HMR for UI-heavy iterations
- Design inspirations: draw.io for interaction affordances; Stan Playground for model/run UX [18], [19].
Comparison to Legacy DoodleBUGS
The legacy tool was a windows desktop application driving WinBUGS [20]; the new DoodleBUGS is a browser-based editor targeting JuliaBUGS [1].
Key differences:
- Platform and backend
- Legacy: Desktop UI, WinBUGS execution pipeline
- New: Web UI, Julia backend via
JuliaBUGS.@bugs
, sampling withAdvancedHMC.NUTS
throughAbstractMCMC
- Graph engine and plates
- Legacy: Bespoke graph handling with limited nesting semantics
- New: Cytoscape.js with compound nodes for robust nested plates; custom drag-and-drop for drag-in/out and creating inside plates
- Layouts and interactions
- Code generation
- Legacy: Export to BUGS without strong ordering guarantees
- New: Deterministic topological + plate-aware traversal; parameter canonicalization and safe index expansion
- Execution and tooling
- Legacy: WinBUGS-managed runs
- New: Lightweight Julia HTTP backend, configurable timeouts, resilient temp cleanup, JSON summaries via
MCMCChains
- DevX and maintainability
- New: Vue 3 + TypeScript + Pinia; unified standalone script generation on the frontend; leaner backend responses
Progress vs Proposal
- Implemented
- Visual editor with nested plates and robust drag-and-drop
- BUGS code generator (topological + plate-aware)
- Local execution + summaries/quantiles
- Unified standalone script generation (frontend)
- Timeouts/resilience
- Multiple layouts and interactions
- Extensive cleanup/typing
- Execution timeout (end-to-end)
- Layout options (Dagre (default, layered), fCoSE (force-directed), Cola (physics simulation), KLay (layered)) and interactions
- Cleanup and stronger typing
- Changed
- Vue 3 instead of React
- Backend responses smaller; no standalone script attachment
- Deferred/Partial
- Visualizations: integrate with MCMCChains.jl for plots (trace, density, PPC, diagnostics). ESS and R-hat already included in summary statistics.
- WebKit/Safari support
- UX polish for large graphs
How to Run Locally
Frontend (Vite):
# from repo root
cd DoodleBUGS
npm install
npm run dev
Backend (Julia):
# from repo root
julia --project=DoodleBUGS/runtime DoodleBUGS/runtime/server.jl
# server listens on http://localhost:8081
Notes:
- CORS is enabled in the backend so the dev UI can call
http://localhost:8081
. - Try it here (static UI): https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/
API Summary for Backend Server
- GET
/api/health
→{ "status": "ok" }
- POST
/api/run
(alias:/api/run_model
)- Body:
model_code
,data
(JSON),inits
(JSON),data_string
(Julia literal),inits_string
(Julia literal),settings
{ n_samples, n_adapts, n_chains, seed, timeout_s }
- Response:
{ success, summary, quantiles, logs, files[] }
wherefiles[]
containsmodel.bugs
,payload.json
,run_script.jl
,results.json
- Note: Frontend falls back to
/api/run_model
if/api/run
is unavailable (404)
- Body:
See DoodleBUGS/runtime/server.jl
.
Current Limitations
- WebKit/Safari/iOS: unsupported at this time (see
DoodleBUGS/README.md
). - Limited visualization beyond summary/quantiles.
- Overlapped plates (nodes with multiple parent plates) are currently not supported; see #362.
Future Work
- Backend: Add Pluto.jl as a backend for supporting compound documents and QuartoNotebookRunner.jl for running notebooks.
- Diagnostics/visualization: integrate with MCMCChains.jl for plots (trace, density, PPC, diagnostics). ESS and R-hat already available in summary stats.
- UX: richer node templates, validation, distribution hints
- Sharing: shareable links/cloud sync (projects already persisted locally)
- Browser compatibility: WebKit/Safari and iOS/iPadOS
- Performance: virtualization for large graphs
Acknowledgements
Much appreciation goes to my mentors Xianda Sun and Hong Ge. The work is impossible without your help and support.
Appendix: Project Links
PRs during GSoC:
- #321 - Add ISSUE template for DoodleBUGS
- #339 - DoodleBUGS Project: Phase 1
- #340 - DoodleBUGS: update all workflows to run on relevent project only
- #341 - Exclude navigation bar from DoodleBUGS project
- #347 - DoodleBUGS: Basic Code Generation, Advanced Exports, and State Persistence
- #357 - DoodleBUGS: Allow Nested Plates, add new layouts and fix lot of linting issues
- #368 - New Folder Structure
- #388 - DoodleBUGS Project: Phase 2 (Backend)
References
[1]
“JuliaBUGS.jl.” Available: https://github.com/TuringLang/JuliaBUGS.jl
[2]
“The BUGS book: A practical introduction to bayesian analysis.” Wiley. Available: https://onlinelibrary.wiley.com/doi/10.1111/anzs.12058
[3]
“The BUGS language.” Available: https://journal.r-project.org/articles/RN-2006-005/RN-2006-005.pdf
[4]
“The BUGS project.” Available: https://www.mrc-bsu.cam.ac.uk/software/bugs/
[5]
“MultiBUGS: Parallel BUGS modeling.” Available: https://pmc.ncbi.nlm.nih.gov/articles/PMC7116196/
[6]
“Bayesian survival analysis with BUGS.” Available: https://onlinelibrary.wiley.com/doi/10.1002/sim.8933
[7]
“Inference via gibbs (albert & chib).” Available: https://apps.olin.wustl.edu/faculty/chib/papers/albertchibjb93.pdf
[8]
“Bayesian inference using gibbs sampling.” Available: https://pubsonline.informs.org/doi/10.1287/ited.2013.0120
[9]
“Cytoscape.js.” Available: https://js.cytoscape.org/
[10]
“ReverseDiff.jl.” Available: https://github.com/JuliaDiff/ReverseDiff.jl
[11]
“AdvancedHMC.jl.” Available: https://github.com/TuringLang/AdvancedHMC.jl
[12]
“AbstractMCMC.jl.” Available: https://github.com/TuringLang/AbstractMCMC.jl
[13]
M. D. Hoffman and A. Gelman, “The no-u-turn sampler: Adaptively setting path lengths in hamiltonian monte carlo,” arXiv preprint arXiv:1111.4246, 2014, Available: https://arxiv.org/abs/1111.4246
[14]
“MCMCChains.jl.” Available: https://github.com/TuringLang/MCMCChains.jl
[15]
“DataFrames.jl.” Available: https://dataframes.juliadata.org/
[16]
“WebCola.” Available: https://ialab.it.monash.edu/webcola/
[17]
“Eclipse layout kernel (ELK / KLay).” Available: https://www.eclipse.org/elk/
[18]
“Draw.io (diagrams.net).” Available: https://www.diagrams.net/
[19]
“Stan playground.” Available: https://stan-playground.flatironinstitute.org/
[20]
“WinBUGS.” Available: http://www.openbugs.net/w/FrontPage