Yorillow
Lightweight, zero-dependency Python chart renderer that generates PNG and SVG charts entirely in Python.
Runs offline, inside Python applications, behind APIs, in Docker, on VPS servers, or in serverless environments.
from yorillow import render
png = render({
"type": "line",
"x": [1, 2, 3, 4],
"y": [[10, 20, 15, 30]],
"title": "Sales",
})
with open("sales.png", "wb") as f:
f.write(png)
No Pillow. No NumPy. No matplotlib. No Chromium. No Node.js. No browser. No database.
Why Yorillow?
| Yorillow | matplotlib | Chart.js | |
|---|---|---|---|
| Runtime dependencies | 0 | numpy, Pillow, … | Node/browser |
| Returns | bytes directly | figure objects | HTML/JS |
| Serverless | native | requires setup | needs browser |
| Import time | ~27 ms | seconds | N/A |
| Memory (800×600) | ~1.8 MiB | ~50 MiB | N/A |
Installation
python -m pip install yorillow
Then:
from yorillow import render
Package name: yorillow (PyPI) / Import name: yorillow (Python)
Quick Start
Render to bytes
from yorillow import render
png = render(config, format="png") # bytes
svg = render(config, format="svg") # str
Save to file
from yorillow import render_to_file
render_to_file(config, "chart.png")
render_to_file(config, "chart.svg")
HTTP response helper
from yorillow import render_response
resp = render_response(config, format="png")
# resp["status_code"] → 200
# resp["content_type"] → "image/png"
# resp["body"] → bytes
Offline Usage
After installation, rendering requires no internet, no API key, no external service.
from yorillow import render
png = render({"type": "line", "x": [1, 2, 3], "y": [[10, 20, 30]]})
Completely offline installation
On an internet-connected machine:
python -m pip download yorillow
Transfer the downloaded file to the offline machine, then:
python -m pip install --no-index --find-links . yorillow
Since yorillow has zero runtime dependencies, only the yorillow package itself needs to be transferred.
Chart Types
Line
render({
"type": "line",
"x": [1, 2, 3, 4, 5],
"y": [[10, 20, 15, 25, 30], [5, 15, 10, 20, 25]],
"labels": ["Revenue", "Profit"],
"title": "Monthly Performance",
"legend": True,
"fill": True,
})
Bar
render({
"type": "bar",
"categories": ["Q1", "Q2", "Q3", "Q4"],
"values": [[120, 150, 170, 200], [90, 130, 140, 180]],
"labels": ["Product A", "Product B"],
"stacked": False,
})
Scatter
render({
"type": "scatter",
"x": [1.0, 2.5, 3.7, 4.2, 5.8],
"y": [10.0, 20.5, 15.3, 25.1, 30.0],
"sizes": [5, 8, 6, 10, 4],
})
Pie / Donut
render({
"type": "pie",
"values": [35, 25, 20, 12, 8],
"labels": ["Chrome", "Firefox", "Safari", "Edge", "Other"],
"donut": True,
})
Area
render({
"type": "area",
"x": [1, 2, 3, 4, 5],
"y": [[10, 20, 15, 25, 30], [5, 10, 8, 12, 15]],
"labels": ["Downloads", "Installs"],
"stacked": True,
})
Histogram
render({
"type": "histogram",
"data": [12.5, 14.2, 15.0, 15.8, 16.1, 17.2, 18.0, 19.1, 20.5, 22.4, 25.0],
"bins": 6,
"title": "Latency Distribution (ms)",
})
Heatmap
render({
"type": "heatmap",
"values": [
[22.4, 25.1, 28.3, 30.2],
[18.2, 21.0, 24.5, 27.8],
[15.1, 17.5, 20.2, 22.9],
],
"x_labels": ["Q1", "Q2", "Q3", "Q4"],
"y_labels": ["North", "Central", "South"],
"color_scale": "viridis",
"show_values": True,
"title": "Regional Temperature Matrix",
})
Box Plot
render({
"type": "box_plot",
"data": [
[15, 18, 20, 22, 23, 24, 25, 27, 29, 32, 45],
[10, 14, 16, 17, 19, 20, 21, 22, 24, 26, 28],
],
"categories": ["Algorithm A", "Algorithm B"],
"show_outliers": True,
"title": "Execution Time (ms)",
})
Candlestick
render({
"type": "candlestick",
"open": [150.0, 153.2, 151.8, 156.4],
"high": [155.0, 156.0, 158.5, 161.0],
"low": [148.5, 150.1, 150.0, 153.8],
"close": [153.2, 151.8, 156.4, 154.0],
"dates": ["Mon", "Tue", "Wed", "Thu"],
"title": "Daily Price Action",
})
Radar / Spider
render({
"type": "radar",
"categories": ["Attack", "Defense", "Speed", "Tactics", "Stamina"],
"values": [
[88, 72, 94, 85, 90],
[75, 92, 78, 88, 82],
],
"labels": ["Player Alpha", "Player Beta"],
"fill": True,
"legend": True,
"title": "Skill Radar",
})
Step
render({
"type": "step",
"x": [0, 1, 2, 3, 4, 5, 6, 7],
"y": [[0, 1, 1, 2, 3, 5, 8, 13]],
"step_where": "post",
"fill": True,
"title": "State Progression",
})
Styling & Customization
Every chart is fully customizable through the same plain-dict config — no new objects, no new dependencies. All options work identically for PNG and SVG.
render({
"type": "line",
"x": [1, 2, 3, 4, 5],
"y": [[10, 20, 15, 30, 26], [5, 12, 9, 18, 14]],
"labels": ["Revenue", "Profit"],
"width": 860, "height": 500, "scale": 2, # high-resolution output
"theme": "corporate", # 13 built-in themes
"background": "transparent", # transparent PNGs
"colors": ["#003f5c", "#ff7c43"], # custom palette
"title": {"text": "Growth", "align": "left", "size": 22, "bold": True},
"subtitle": "FY2026",
"caption": "Source: internal",
"margin": 12,
"legend": {"show": True, "position": "bottom", "marker": "circle"},
"grid": {"x": False, "dash": [3, 3], "opacity": 0.6},
"axes": {"x": {"label": "Month"},
"y": {"label": "USD", "min": 0, "format": "{:,.0f}"}},
"series": [{"line_width": 3, "fill": True, "fill_opacity": 0.15},
{"dash": [6, 3]}],
"annotations": [{"type": "hline", "y": 25, "label": "Target"}],
})
| Area | Options |
|---|---|
| 🎨 Themes | 13 built-ins, inline dict themes, Theme objects, custom registry |
| 🖌️ Colors | Palettes by name or list, per-series colors, custom backgrounds |
| 📐 Size | width, height, scale (1–8) for high-resolution export |
| 🔤 Text | Family, size, weight, italic, letter-spacing, color, rotation |
| 📝 Labels | Title, subtitle, caption, axis titles, annotations |
| 📏 Layout | margin, padding, automatic space reservation |
| 🧭 Legend | 8 positions, orientation, markers, box styling |
| 📊 Axes/grid | Ticks, formats, prefixes, min/max, dashes, per-axis control |
| 🖼️ Transparency | "background": "transparent" (alpha-0 PNG, bare SVG) |
Full reference: docs/api/styling.md · Cookbook: docs/guides/customization.md
Themes
default / light |
dark |
minimal |
neon |
corporate |
mono |
solarized |
pastel |
ocean |
high_contrast |
midnight |
print |
render({**config, "theme": "dark"})
# inline theme, no global state
render({**config, "theme": {"base": "dark", "palette": "ocean"}})
# reusable theme object
from yorillow import Theme
brand = Theme(name="brand", palette=["#003366", "#0066cc"], title_color="#003366")
render({**config, "theme": brand})
More: docs/api/themes.md
JSON Configuration
Chart configurations are plain JSON — no Python objects required.
{
"type": "line",
"x": [1, 2, 3, 4],
"y": [[10, 20, 15, 30]],
"title": "Sales"
}
import json
from yorillow import render
with open("chart.json") as f:
config = json.load(f)
png = render(config)
This makes Yorillow useful when configuration comes from another program, a database, an API, or another language.
CLI
yorillow chart.json -o chart.png # render PNG
yorillow chart.json -o chart.svg # render SVG
cat chart.json | yorillow -o chart.png # stdin
yorillow chart.json --base64 # JSON with base64 data
yorillow chart.json --validate-only # validate only
yorillow chart.json -o out.png --bench # show timing
yorillow chart.json -o out.png --theme midnight --scale 2 --transparent
yorillow chart.json -o out.svg --title "Q1" --width 900 --height 500
yorillow --list-themes # list available themes
yorillow --version
yorillow --help
PowerShell:
Get-Content chart.json -Raw | yorillow -o chart.png
Build an API
Yorillow is the renderer, not the server.
Client → JSON → HTTP Server → yorillow.render() → PNG/SVG → HTTP Response
stdlib (zero dependencies)
python examples/api/simple_http_server.py
curl -X POST http://localhost:8080/chart \
-H "Content-Type: application/json" \
-d '{"type":"line","x":[1,2,3],"y":[[10,20,30]]}' \
--output chart.png
Flask
pip install flask yorillow
python examples/api/flask_app.py
FastAPI
pip install fastapi uvicorn yorillow
python -m uvicorn examples.api.fastapi_app:app
Deploy
| Environment | Guide | Status |
|---|---|---|
| Local Python | Offline Usage | ✓ Tested |
| CLI | CLI | ✓ Tested |
| VPS | docs/deployment/vps.md | ✓ Tested |
| Docker | docs/deployment/docker.md | ✓ Tested |
| Flask | examples/api/flask_app.py | Example |
| FastAPI | examples/api/fastapi_app.py | Example |
| Render | docs/deployment/render.md | Example |
| Railway | docs/deployment/railway.md | Example |
| Vercel | docs/deployment/vercel.md | Example |
| Netlify | docs/deployment/netlify.md | Example |
| AWS Lambda | docs/deployment/aws-lambda.md | Example |
Serverless Architecture
The core renderer is stateless and dependency-free, making it suitable for Python-compatible serverless runtimes.
Request → render() → PNG/SVG → Response
No persistent process. No database. No filesystem. No browser.
See docs/architecture/serverless.md.
Performance
| Metric | Value |
|---|---|
| Import | ~27 ms |
| PNG 800×600 | ~15–60 ms |
| SVG 800×600 | <0.4 ms |
| PNG 1920×1080 | ~60 ms |
| Canvas 800×600 | 1.8 MiB |
Run python benchmarks/bench.py to reproduce.
See docs/performance.md.
Security
Yorillow validates all inputs: max dimensions, data point limits, NaN/Infinity rejection, type checking.
When exposing publicly, add authentication, rate limiting, and request size limits at the HTTP layer.
See docs/security.md.
API Reference
| Function | Returns | Description |
|---|---|---|
render(config, format, encoding) |
bytes/str |
Stateless chart renderer |
render_to_file(config, path) |
None |
Render and save to file |
render_response(config, format) |
dict |
HTTP response helper |
| Class | Description |
|---|---|
Canvas |
Low-level drawing surface |
Theme |
Colour/font bundle (get_theme, list_themes, resolve_theme) |
ChartStyle |
Styling container for the object API |
ResolvedStyle |
Fully-resolved appearance shared by both renderers |
Layout / Rect |
Computed chart geometry (compute_layout) |
Annotation |
Parsed annotation |
LineChart |
Line chart |
BarChart |
Bar chart |
ScatterChart |
Scatter plot |
PieChart |
Pie / donut chart |
AreaChart |
Area chart |
HistogramChart (Histogram) |
Histogram chart |
HeatmapChart (Heatmap) |
Heatmap matrix |
BoxPlotChart (BoxPlot) |
Box plot (whisker plot) |
CandlestickChart (Candlestick) |
Candlestick OHLC chart |
RadarChart (Radar, SpiderChart) |
Radar / spider chart |
StepChart (Step) |
Step-wise line chart |
| Exception | Description |
|---|---|
YorillowError |
Base exception |
ValidationError |
Invalid input |
RenderError |
Internal error |
Full reference: docs/api/
Examples
Development
git clone https://github.com/harshi79/yorillow.git
cd yorillow
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
python -m pytest tests/ -v
See CONTRIBUTING.md.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yorillow-1.1.0.tar.gz.
File metadata
- Download URL: yorillow-1.1.0.tar.gz
- Upload date:
- Size: 143.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ac84bceb0d61b9562076f89ad185b49b357de285f60aac9c32db931eec5d08e
|
|
| MD5 |
f0fb67b657886181e3b146c0d6c1d330
|
|
| BLAKE2b-256 |
0a3e1dc49402198f5751fb83855edcb105fc2ea36277ac34487edfd24a5f2776
|
File details
Details for the file yorillow-1.1.0-py3-none-any.whl.
File metadata
- Download URL: yorillow-1.1.0-py3-none-any.whl
- Upload date:
- Size: 86.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
551697fe370549cd9640bebc7258cc5db85fc26d192643da8e2feda28d42d5c0
|
|
| MD5 |
66a1ccfe247a12a3ab54c889b0e7d394
|
|
| BLAKE2b-256 |
97c880bb1a9aaaa6eda02d4873437cf79d495f28342c0c224b8a7b596efc7332
|