Skip to content

Instantly share code, notes, and snippets.

@Siddhant-K-code
Created September 7, 2025 04:44
Show Gist options
  • Save Siddhant-K-code/d6e0a4f9f818f5af87a15b8c96b32a7f to your computer and use it in GitHub Desktop.
Save Siddhant-K-code/d6e0a4f9f818f5af87a15b8c96b32a7f to your computer and use it in GitHub Desktop.
Beautiful MatplotLib's xkcd graph
#!/usr/bin/env python3
# make_chalk_plots.py
# Hand-drawn (xkcd) style charts; saves stacked + per-panel images.
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as pe
# --------- Output ----------
OUTDIR = Path("images/xkcd")
DPI = 300
# --------- Theme ----------
BG = "#0c0c0c" # chalkboard
FG = "white" # chalk
LINE = "#ff3b3b" # red chalk
def apply_global_rc():
plt.rcParams.update({
"figure.facecolor": BG,
"axes.facecolor": BG,
"axes.edgecolor": FG,
"axes.labelcolor": FG,
"xtick.color": FG,
"ytick.color": FG,
"grid.color": FG,
"grid.linewidth": 2.2,
"axes.linewidth": 3.0,
"font.size": 14,
"svg.fonttype": "none", # keep text as text in SVG
})
def style_axes(ax):
ax.grid(True)
for s in ax.spines.values():
s.set_linewidth(3.2)
s.set_color(FG)
ax.margins(x=0.02, y=0.15)
def plot_line(ax, x, y):
ax.plot(
x, y, lw=3.2, color=LINE,
marker="o", ms=7,
markerfacecolor=BG, markeredgecolor=FG, markeredgewidth=2.5,
path_effects=[pe.Stroke(linewidth=6, foreground=FG), pe.Normal()],
)
def savefig(fig, name):
OUTDIR.mkdir(parents=True, exist_ok=True)
png = OUTDIR / f"{name}.png"
svg = OUTDIR / f"{name}.svg"
fig.savefig(png, dpi=DPI, bbox_inches="tight", facecolor=fig.get_facecolor())
fig.savefig(svg, bbox_inches="tight", facecolor=fig.get_facecolor())
return png, svg
def slugify(s: str) -> str:
return "".join(c.lower() if c.isalnum() else "-" for c in s).strip("-").replace("--", "-")
# --------- Data ---------
# Replace values with your real measurements if needed.
years = np.array([1980, 1990, 2000, 2005, 2010, 2015, 2020, 2025])
series = [
# --- original five ---
{"title": "Transistor Count / Socket", "ylabel": "Transistors (Billions)",
"values": [0, 0.1, 0.3, 0.6, 2, 30, 70, 90]},
{"title": "Core Count / Socket", "ylabel": "Cores",
"values": [1, 1, 1, 2, 8, 32, 64, 96]},
{"title": "Vector GFLOPS", "ylabel": "GFLOPS",
"values": [0, 0.5, 1, 5, 100, 2000, 3000, 3800]},
{"title": "Memory BW", "ylabel": "GB/s",
"values": [0, 0.5, 1, 4, 50, 140, 320, 480]},
{"title": "Total Cache Size", "ylabel": "MB",
"values": [0.1, 0.5, 1, 2, 24, 192, 256, 320]},
# --- new two (from your image) ---
{"title": "Storage BW / drive", "ylabel": "GB/s",
"values": [0.02, 0.04, 0.08, 0.12, 0.6, 3.5, 5.5, 7.2],
"ylim": (0, 7.5), "yticks": [0, 2, 4, 6]},
{"title": "Network BW", "ylabel": "GB/s",
"values": [0.02, 0.05, 0.2, 0.6, 5, 12, 30, 50],
"ylim": (0, 52), "yticks": [0, 10, 20, 30, 40, 50]},
]
# --------- Main ----------
def make_all():
apply_global_rc()
with plt.xkcd(scale=1.0, length=200, randomness=2.5):
# Stacked (all panels)
fig, axes = plt.subplots(len(series), 1, figsize=(8, 18), sharex=True)
for ax, s in zip(axes, series):
y = np.array(s["values"], dtype=float)
plot_line(ax, years, y)
ax.set_title(s["title"])
ax.set_ylabel(s["ylabel"])
if "ylim" in s: ax.set_ylim(*s["ylim"])
if "yticks" in s: ax.set_yticks(s["yticks"])
style_axes(ax)
axes[-1].set_xlabel("Year")
savefig(fig, "stacked_all")
plt.close(fig)
# Individual panel figures
for idx, s in enumerate(series, start=1):
fig, ax = plt.subplots(figsize=(7.2, 4.5))
y = np.array(s["values"], dtype=float)
plot_line(ax, years, y)
ax.set_title(s["title"])
ax.set_xlabel("Year")
ax.set_ylabel(s["ylabel"])
if "ylim" in s: ax.set_ylim(*s["ylim"])
if "yticks" in s: ax.set_yticks(s["yticks"])
style_axes(ax)
savefig(fig, f"panel_{idx:02d}_{slugify(s['title'])}")
plt.close(fig)
if __name__ == "__main__":
make_all()
print(f"Saved images to: {OUTDIR.resolve()}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment