Panels is a comic strip browser built with Rust. Pick your favorite strips, fetch a random panel instantly, doomscroll your favourites, and get recommendations based on what you already like.
Panels works best when you run it on your own machine or on a host you control, for complete access to all sources like GoComics.
Note
Panels is a personal project and is not affiliated with any comic publishers. All comics are sourced from publicly available data and are intended for personal use and enjoyment. All comics are property of their respective creators and publishers.
A live demo is available at panels.ashmod.dev.
The demo runs a lightweight deployment without the GoComics browser integration, so it includes full collections for a subset of comics sourced through alternative providers:
- Calvin and Hobbes
- Peanuts
- Garfield
- xkcd
- Dilbert
- Piled Higher and Deeper
A handful of other series have their most recent strips available via RSS.
To access the full catalog, run Panels locally or self-host it.
- Rust stable toolchain
cargonodenpm
Panels uses Playwright for GoComics pages, so install the Node dependency once:
npm installThis runs the package postinstall step, which downloads the Playwright browser used by the GoComics fallback.
cargo runPanels starts on http://localhost:3000 by default.
Visit http://localhost:3000 in your browser.
If you just want to confirm the backend is up:
curl http://localhost:3000/api/healthExpected response:
{"status":"ok"}You can also specify the port with:
cargo run -- --port PORT_NUMPanels uses CLI flags and environment variables:
| Flag | Env | Default | Description |
|---|---|---|---|
--port |
PANELS_PORT |
3000 |
HTTP server port |
--data-dir |
PANELS_DATA_DIR |
data |
Path containing comics.json, tags.json, and badges/ |
--strip-cache-max |
PANELS_STRIP_CACHE_MAX |
500 |
Max strip cache entries |
--strip-cache-ttl |
PANELS_STRIP_CACHE_TTL |
1800 |
Strip cache TTL in seconds |
| n/a | PANELS_GOCOMICS_BROWSER |
unset | Optional browser executable path for the GoComics Playwright fallback |
Example:
PANELS_PORT=4000 PANELS_DATA_DIR=./data cargo runIf Playwright should use a specific browser binary:
PANELS_GOCOMICS_BROWSER=/path/to/browser cargo runBasic liveness endpoint.
Returns comics with tag metadata.
Query params:
search(optional): matchestitleorendpointtag(optional): exact tag filter (case-insensitive)
Example:
curl "http://localhost:3000/api/comics?search=garfield&tag=humor"Returns scored recommendations from selected comic endpoints.
Query params:
selected(required for non-empty results): comma-separated endpointslimit(optional): max results, default10
Example:
curl "http://localhost:3000/api/recommendations?selected=garfield,peanuts&limit=8"Returns one strip as JSON.
{date} supports:
YYYY-MM-DDlatestrandom
Examples:
curl "http://localhost:3000/api/comics/garfield/latest"
curl "http://localhost:3000/api/comics/garfield/random"
curl "http://localhost:3000/api/comics/garfield/2025-02-14"Proxies the strip image bytes and content type.
Caching behavior:
date=random:Cache-Control: no-store- any non-random request:
Cache-Control: public, max-age=86400, s-maxage=604800
Example:
curl -I "http://localhost:3000/api/comics/garfield/random/image"Typical local workflow:
npm install
cargo runBefore opening a PR, run:
cargo fmt
cargo clippy --all-targets -- -D warnings
cargo test --all-targetsCI (.github/workflows/ci.yml) runs:
cargo check --all-targetscargo test --all-targets
Tests workflow (.github/workflows/tests.yml) runs:
cargo test --all-targets
Contributions are welcome. Open an issue or send a PR if a comic is missing, a source breaks, or you have an improvement in mind.
When reporting an issue, please include:
- A clear description of the problem.
- Steps to reproduce the issue.
- Expected vs actual behavior.
- Any relevant logs or error messages.
- Fork the repo and create a feature branch.
- Make focused changes with clear commit messages.
- Run the local quality checks.
- Open the PR with context and testing notes.
cargo fmt
cargo clippy --all-targets -- -D warnings
cargo test --all-targets- Scope is limited to one feature or fix.
- API behavior changes are documented in
README.md. - New behavior is covered by tests in
tests/or module tests. - Clippy and tests pass locally.
MIT. See LICENSE.