Skip to the content.

πŸš€ Dynamic Progress Bar

Generates a dynamic progress bar image (SVG) using Python, deployed freely via Vercel β€” or self-hosted on your own server.

πŸ“Š Preview:

Progress Progress Progress

Inspired by fredericojordan/progress-bar.


πŸ”₯ Other Projects


πŸ› οΈ Usage

This service is deployed on Vercel and accessible via the domain progress-bar.xyz.


βš™οΈ Parameters

πŸ”§ Parameter πŸ“œ Description 🎯 Default Value
title Adds a title to the progress bar None
scale The maximum value that the progress bar represents 100
as_percent With scale, show the number as a percentage of the scale false
prefix A string to add before the progress number None
suffix A string to add after the progress number %
width The width of the progress bar in pixels 60 with a title, 90 without
color The color of the title area (hex code without #) 428bca (blue)
progress_background The background color of the progress bar (hex code without #) 555 (grey)
progress_number_color The color of the progress number (hex code without #) fff (white)
progress_color The color of the progress bar (hex code without #) Depends on percentage
show_text Whether to display or hide the progress text true
show_shadow Whether to draw the text shadow true
radius Corner radius in pixels 4
style The style. One of: default, flat, square, plastic, for-the-badge default

Examples

Below are several examples showcasing different ways to generate progress bars.

πŸ“Œ Preview 🌐 URL
Progress https://progress-bar.xyz/28/
Progress https://progress-bar.xyz/28/?title=progress
Progress https://progress-bar.xyz/58/
Progress https://progress-bar.xyz/58/?title=completed
Progress https://progress-bar.xyz/91/
Progress https://progress-bar.xyz/91/?title=done
Progress https://progress-bar.xyz/180/?scale=10&title=mark&prefix=R$&suffix=
Progress https://progress-bar.xyz/420/?scale=500&title=funds&width=200&color=babaca&prefix=R$&suffix=
Progress https://progress-bar.xyz/7/?scale=10&title=mark&suffix=X
Progress https://progress-bar.xyz/420/?scale=500&title=funds&width=200&color=babaca&suffix=$
Progress https://progress-bar.xyz/58/?title=colorful&progress_background=ffc0cb&progress_number_color=000
Progress https://progress-bar.xyz/100/?progress_color=ff3300
Progress https://progress-bar.xyz/100/?width=100&title=Fixed+color&progress_color=ff3300
Progress https://progress-bar.xyz/28/?show_text=false
Progress https://progress-bar.xyz/90/?show_text=false
Progress from JSON https://progress-bar.xyz/dynamic/json/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fguibranco%2Fprogressbar%2Fmain%2Fdocs%2Fdynamic-json-sample.json&query=demo.metrics.translationPercent&title=JSON%20Demo
Progress from JSON https://progress-bar.xyz/dynamic/json/?url=https%3A%2F%2Fraw.githubusercontent.com%2Fguibranco%2Fprogressbar%2Fmain%2Fdocs%2Fdynamic-json-sample.json&query=demo.metrics.translationPercent

🎨 Styles

We currently support:

🎨 Style πŸ“Œ Preview 🌐 URL
🟒 flat (default) Progress https://progress-bar.xyz/100/?style=flat
πŸ”² square Progress https://progress-bar.xyz/100/?style=square
πŸ›‘οΈ plastic Progress https://progress-bar.xyz/100/?style=plastic
πŸ”– for-the-badge Progress https://progress-bar.xyz/100/?style=for-the-badge
🎭 thin-rounded (WIP) Progress https://progress-bar.xyz/100/?style=thin-rounded
πŸͺž neo-glass (WIP) Progress https://progress-bar.xyz/100/?style=neo-glass
🎨 minimal-matte (WIP) Progress https://progress-bar.xyz/100/?style=minimal-matte

πŸ“‘ Progress from a JSON URL

Besides a fixed value in the path (/75/), you can load the number from a remote JSON file.

Sample JSON file: https://raw.githubusercontent.com/guibranco/progressbar/main/docs/dynamic-json-sample.json

Use query parameters:

Parameter Description
url Required. Address of the JSON document (http or https). Must be URL-encoded in the query string.
query Required. JSONPath (e.g. $.items[0].metrics.pct) or dot form from the root (e.g. items.0.metrics.pct). Filter expressions are supported, so you can look up a value by a sibling field instead of a hardcoded index β€” e.g. $.progress[?(@.data.language.name=='Spanish')].data.translationProgress.
cache Optional. Cache-Control max-age in seconds (alias cacheSeconds), between 0 and 86400. Default: 300. 0 disables caching for that request.

Any other parameters from the table above (title, scale, style, …) apply the same way as on /{number}/.


⚑ Caching

Badges are fetched by GitHub’s image proxy (camo), browsers and CDNs on every README render, so every response carries a Cache-Control header:

Route Cache-Control
/{number}/ public, max-age=86400, s-maxage=86400, stale-while-revalidate=604800 β€” the SVG is fully determined by the URL, so it is safe to cache for a day
/dynamic/json/ public, max-age=300, s-maxage=300 by default; override per request with cache=
Errors, /health no-store

The two defaults can be changed per deployment with the environment variables PROGRESSBAR_STATIC_CACHE_SECONDS and PROGRESSBAR_DYNAMIC_CACHE_SECONDS (0–86400). Keep in mind that changes to a badge’s look can take up to the static TTL (one day) to show up in READMEs β€” and a CDN that honours stale-while-revalidate may keep serving the previous version for up to another 7 days while it refreshes the entry in the background.


πŸš€ Deployment

β–² Vercel

Deploy this project to Vercel with a single click:

Deploy with Vercel

The Hobby plan counts every request (cached or not) against its monthly quota, so a popular badge can hit the limit and get the deployment paused. The Cache-Control headers above keep most renders away from Vercel; if that is still not enough, self-host it.

🏠 Self-hosting

The same code runs anywhere Python runs. The repository ships everything needed for a small VM (1 vCPU / 1 GB is plenty β€” the app is stateless and serves ~1.8 KB per badge in about a millisecond):

File Purpose
Dockerfile, compose.yaml gunicorn (threaded workers) on 127.0.0.1:8099, non-root, with a Docker healthcheck
gunicorn.conf.py Sizing/logging knobs, all overridable via PROGRESSBAR_* environment variables (deploy/env.example)
deploy/nginx/ NGINX reverse proxy with a response cache (proxy_cache), so Python only runs on cache misses
deploy/systemd/ systemd units for the compose stack, or for bare-metal gunicorn without Docker
deploy/install.sh, deploy/update.sh First-time setup and redeploy scripts for the VM
.github/workflows/deploy.yml Optional CI deploy over SSH after CI passes on main (opt-in via SELF_HOST_DEPLOY=true)

Quick start on an Ubuntu host with Docker and NGINX installed (DOMAIN= and APP_DIR= can be exported first to override the defaults):

curl -fsSLO https://raw.githubusercontent.com/guibranco/progressbar/main/deploy/install.sh
less install.sh                            # review before running it as root
sudo bash install.sh
sudo certbot --nginx -d progress-bar.xyz   # TLS

Or without Docker:

python -m venv venv && venv/bin/pip install -r requirements.txt
venv/bin/gunicorn -c gunicorn.conf.py app:app   # http://127.0.0.1:8099/58/

GET /health returns {"status": "ok"} for uptime monitors and load balancers. Put the domain behind a CDN/proxy (for example Cloudflare’s free plan) to get edge caching and to be able to switch origins without touching the app.


🀝 Contributing

Check out CONTRIBUTING.md to learn how to contribute!

πŸ‘₯ Contributors

guibranco
Guilherme Branco Stracini
nimble-turtle
nimble-turtle
ztest95
ztest95
Pointbr8ker-123
David Nwosu
EduardoBaptista01
Eduardo Baptista
bhosley
bhosley

πŸ€– Bots

dependabot[bot]
dependabot[bot]
github-actions[bot]
github-actions[bot]
penify-dev[bot]
penify-dev[bot]
deepsource-autofix[bot]
deepsource-autofix[bot]
snyk-bot
Snyk bot

πŸ“œ License

This project is licensed under the MIT License.

πŸ“„ See LICENSE for details.