Stacktower
Visualize package dependencies as XKCD-style tower diagrams. Supports Python, Rust, JavaScript, Ruby, PHP, Java, and Go.
Install / Use
/learn @matzehuels/StacktowerREADME
Stacktower
Inspired by XKCD #2347, Stacktower renders dependency graphs as physical towers where blocks rest on what they depend on. Your application sits at the top, supported by libraries below—all the way down to that one critical package maintained by some dude in Nebraska.
<p align="center"> <img src="blogpost/plots/showcase/python/fastapi.svg" alt="FastAPI dependency tower" width="600"> </p><p align="center"> <a href="https://app.stacktower.io"><strong>🚀 Try the Web App</strong></a> · <a href="https://www.stacktower.io"><strong>📖 Read the Story</strong></a> </p>
⚠️ Note: The ordering algorithms are still experimental and may not produce nicely stacked towers for projects with a large number of dependencies. Results can vary. We're actively working on improvements.
Installation
Homebrew (macOS/Linux)
brew install matzehuels/tap/stacktower
Go
go install github.com/matzehuels/stacktower@latest
From Source
git clone https://github.com/matzehuels/stacktower.git
cd stacktower
go build -o bin/stacktower ./cmd/stacktower
Quick Start
# Render the included Flask example (XKCD-style tower is the default)
stacktower render examples/real/flask.json -o flask.svg
Usage
Stacktower works in two stages: parse dependency data from package registries or manifest files, then render visualizations.
Parsing Dependencies
The parse command auto-detects whether you're providing a package name or a manifest file:
stacktower parse <language> <package-or-file> [flags]
Supported languages: python, rust, javascript, ruby, php, java, go
From Package Registries
stacktower parse python fastapi -o fastapi.json # PyPI
stacktower parse rust serde -o serde.json # crates.io
stacktower parse javascript yargs -o yargs.json # npm
stacktower parse ruby rails -o rails.json # RubyGems
stacktower parse php monolog/monolog -o monolog.json # Packagist
stacktower parse java com.google.guava:guava -o guava.json # Maven Central
stacktower parse go github.com/gin-gonic/gin -o gin.json # Go Module Proxy
From Manifest Files (Auto-Detected)
stacktower parse python examples/manifest/poetry.lock -o deps.json
stacktower parse python examples/manifest/requirements.txt -o deps.json
stacktower parse rust examples/manifest/Cargo.toml -o deps.json
stacktower parse javascript examples/manifest/package.json -o deps.json
stacktower parse ruby examples/manifest/Gemfile -o deps.json
stacktower parse php examples/manifest/composer.json -o deps.json
stacktower parse java examples/manifest/pom.xml -o deps.json
stacktower parse go examples/manifest/go.mod -o deps.json
When the argument exists on disk or matches a known manifest filename, Stacktower automatically parses it as a manifest.
The project name (root node) is auto-detected from the manifest or a sibling file:
- Cargo.toml:
[package].name - go.mod:
moduledirective - package.json:
namefield - composer.json:
namefield - pom.xml:
groupId:artifactId - poetry.lock / requirements.txt:
pyproject.toml(sibling) - Gemfile:
*.gemspec(sibling)
Use --name to override the auto-detected name:
stacktower parse python requirements.txt --name="my-project" -o deps.json
stacktower parse ruby Gemfile -n my-rails-app -o deps.json
Metadata Enrichment
By default, Stacktower enriches packages with GitHub metadata (stars, maintainers, last commit) for richer visualizations. Set GITHUB_TOKEN to enable this:
export GITHUB_TOKEN=your_token
stacktower parse python fastapi -o fastapi.json
# Skip enrichment if you don't have a token
stacktower parse python fastapi --skip-enrich -o fastapi.json
Rendering
The render command generates visualizations from parsed JSON graphs:
stacktower render <file> [flags]
This is a shortcut that combines layout and visualize in one step. For more control, you can run them separately:
# Two-step workflow with intermediate layout
stacktower layout examples/real/flask.json -o flask.layout.json
stacktower visualize flask.layout.json -o flask.svg
Visualization Types
# Hand-drawn XKCD-style tower (default)
stacktower render examples/real/flask.json -o flask.svg
# Disable hand-drawn effects for a cleaner look
stacktower render examples/real/serde.json --style simple --randomize=false --popups=false -o serde.svg
# Traditional node-link diagram (uses Graphviz DOT)
stacktower render examples/real/yargs.json -t nodelink -o yargs.svg
Output Formats
# SVG output (default)
stacktower render examples/real/flask.json -o flask.svg
# JSON layout export (for external tools or re-rendering)
stacktower render examples/real/flask.json -f json -o flask.json
# PDF output
stacktower render examples/real/flask.json -f pdf -o flask.pdf
# PNG output (2x scale by default)
stacktower render examples/real/flask.json -f png -o flask.png
# Multiple formats at once (outputs flask.svg, flask.json, flask.pdf)
stacktower render examples/real/flask.json -f svg,json,pdf -o flask
Output path behavior:
- No
-o: Derives from input (input.json→input.<format>) - Single format: Uses exact path (
-o out.svg→out.svg) - Multiple formats: Strips extension, adds format (
-o out -f svg,json→out.svg,out.json)
Note: PDF and PNG output requires librsvg:
- macOS:
brew install librsvg- Linux:
apt install librsvg2-bin
Included Examples
The repository ships with pre-parsed graphs so you can experiment immediately:
# Real packages with full metadata (XKCD-style by default)
stacktower render examples/real/flask.json -o flask.svg
stacktower render examples/real/serde.json -o serde.svg
stacktower render examples/real/yargs.json -o yargs.svg
# With Nebraska guy ranking
stacktower render examples/real/flask.json --nebraska -o flask.svg
# Synthetic test cases
stacktower render examples/test/diamond.json -o diamond.svg
Options Reference
Global Options
| Flag | Description |
| ----------------- | -------------------------------------------------------- |
| -v, --verbose | Enable debug logging (search space info, timing details) |
Parse Options
| Flag | Description |
| ---------------- | -------------------------------------------------------------------------- |
| -o, --output | Output file (stdout if empty) |
| -n, --name | Project name for manifest parsing (auto-detected from manifest if not set) |
| --max-depth N | Maximum dependency depth (default: 10) |
| --max-nodes N | Maximum packages to fetch (default: 5000) |
| --skip-enrich | Skip metadata enrichment (GitHub descriptions, etc.) |
| --no-cache | Disable caching |
Render Options
| Flag | Description |
| ---------------- | --------------------------------------------------------------------------------------------------------------------- |
| -o, --output | Output file or base path for multiple formats |
| -t, --type | Visualization type: tower (default), nodelink |
| -f, --format | Output format(s): svg (default), json, pdf, png (comma-separated) |
| --normalize | Apply graph normalization: break cycles, remove transitive edges, assign layers, subdivide long edges (default: true) |
Tower Options
| Flag | Description |
| --------------------------------- | --------------------------------------------------------------------- |
| --width N | Frame width in pixels (default: 800) |
| --height N | Frame height in pixels (default: 600) |
| --style handdrawn\|simple | Visual style (default: handdrawn) |
| --randomize | Vary block widths to visualize load-bearing structure (default: true) |
| --merge | Merge subdivider blocks into continuous towers (default: true) |
| --popups | Enable hover popups with package metadata (default: true) |
| --nebraska | Show "Nebraska guy" maintaine
