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 artifactsMotivation
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/runwith body:model_code(BUGS),dataandinits(JSON),data_stringandinits_string(Julia NamedTuple literals), andsettings{ n_samples, n_adapts, n_chains, seed, timeout_s }. If/api/runreturns 404, it falls back to/api/run_model. - Backend creates a temp dir, writes
model.bugsandpayload.json, generates an ephemeralrun_script.jl, compiles withJuliaBUGS.@bugs, wraps withADgradient(:ReverseDiff), and samples viaAdvancedHMC.NUTSthroughAbstractMCMC(Threads or Serial). It writes summaries (incl. ESS, R-hat) and quantiles to JSON and returns{ success, summary, quantiles, logs, files[] }, wherefilesincludesmodel.bugs,payload.json,run_script.jl, andresults.json. - Frontend also generates a
standalone.jlscript 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/runand/api/run_model - Execution: creates temp dir, writes
model.bugsandpayload.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.tsand mapsGraphNode/GraphEdgeto 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.jlmaterializesmodel.bugs,payload.json, and a transientrun_script.jlthat:- Builds
NamedTuples from JSON or string-literal data/inits - Compiles via
JuliaBUGS.@bugs, wraps withADgradient(:ReverseDiff)[10] - Samples with
AdvancedHMC.NUTSthroughAbstractMCMC(Threads or Serial) [11], [12], [13] - Emits summaries (incl. ESS and R-hat) via
MCMCChains/DataFramesand 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.NUTSthroughAbstractMCMC
- 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 devBackend (Julia):
# from repo root
julia --project=DoodleBUGS/runtime DoodleBUGS/runtime/server.jl
# server listens on http://localhost:8081Notes:
- 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_modelif/api/runis 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