Memes with Python's matplotlib. Create image-macro memes by either letting the memegen API render them server-side (the default) or falling back to a local Pillow renderer when the API can't express what you want — custom local images, explicit per-line font sizes, custom outlines, or per-line position overrides.
Installation
pip install memeplotlib
# or
conda install -c conda-forge memeplotlib
For the Model Context Protocol server (use memes from Claude Desktop / Claude Code / any MCP client):
pip install "memeplotlib[mcp]"
Quick start
import memeplotlib as memes
fig, ax = memes.meme("buzz", "memes", "memes everywhere")
fig.savefig("buzz.png")
The function returns (Figure, Axes) — same convention as seaborn,
pandas.plot, and other matplotlib extensions. It does not call
plt.show() implicitly. Pass show=True if you want auto-display.
Features
Memegen IDs, file paths, or URLs as templates:
memes.meme("drake", "writing tests", "shipping to prod", color="yellow")
memes.meme("/path/to/image.jpg", "top text", "bottom text")
memes.meme("https://example.com/image.png", "from a URL")
Object-oriented Meme builder, chainable:
from memeplotlib import Meme
Meme("buzz").top("python").bottom("python everywhere").save("buzz.png")
Memify existing matplotlib figures:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
memes.memify(fig, "stonks")
RcParams-style scoped configuration:
with memes.rc_context({"font": "comic", "color": "yellow"}):
memes.meme("buzz", "scoped style", "no leakage")
# defaults are auto-restored here
# Or set globally:
memes.config["fontsize"] = 96
Forward **kwargs to Axes.text:
memes.meme("buzz", "rotated", rotation=15, alpha=0.8)
Backends
memeplotlib ships three rendering backends. The default "auto" policy
picks the best fit; pass backend="..." to override.
| Backend | What it does | Honours |
|---|---|---|
memegen |
Builds a memegen rendering URL and imshows the response. No client-side text drawing. |
font, color, style, template_style, width, height, layout, background, overlays, extension (png/jpg/gif/webp) |
pillow |
Downloads the blank, draws captions client-side with PIL.ImageDraw. Used for custom local images and any feature memegen can't express. |
All caption styling including per-line fontsize, custom outline_color / outline_width, and per-line overrides via Meme.line(...). |
matplotlib |
Legacy: draws captions with Axes.text + patheffects.Stroke. Kept for backwards compatibility. |
All caption styling plus **kwargs forwarded to Axes.text (e.g. rotation, alpha). |
backend="auto" selects:
memegen— when the template comes from the memegen catalogue and the caller didn't passfontsize, a non-defaultoutline_color/outline_width,**text_kwargs, or per-line overrides.pillow— for custom local images / arbitrary URLs, or when any of the above client-only features were requested.
# Default: memegen renders this server-side.
memes.meme("buzz", "memes", "memes everywhere", template_style="default",
font="impact", width=600)
# Pillow fallback — explicit fontsize forces it.
memes.meme("/path/to/photo.jpg", "top", "bottom", fontsize=48,
outline_color="red", outline_width=4)
# Per-line overrides force the Pillow backend.
from memeplotlib import Meme
Meme("buzz").top("hi").line(1, "world", fontsize=72, color="yellow").save("out.png")
# Legacy matplotlib path (preserves old behaviour exactly):
memes.meme("buzz", "rotated", rotation=15, alpha=0.8, backend="matplotlib")
See docs/url_construction.rst for the full
memegen URL grammar — escape table, query parameters, font / style / overlay
reference — adapted from jacebrowning/memegen #993.
Use from agents
pip install "memeplotlib[mcp]"
memeplotlib-mcp # boot the MCP server (stdio)
memeplotlib meme buzz "hello" "world" -o /tmp/ # CLI render
The MCP server exposes meme, search_templates, and list_templates
tools. The CLI is useful even without MCP — agent harnesses can shell
out, and CI scripts can render directly.
Documentation
Full docs including a tutorial, user guide, conventions reference, and API reference: brianckeegan.github.io/memeplotlib.
Build locally:
pip install -e ".[docs]"
sphinx-build -W docs docs/_build
How it works
- Template metadata comes from the memegen API
(
/templates/,/templates/<id>); blank backgrounds and rendered memes alike are cached on disk. - Memegen backend (default for memegen IDs): a fully-formed rendering
URL (
/images/<id>/<line_1>/.../<line_n>.<ext>?style=...&font=...) is built viamemeplotlib.build_memegen_url. The composed image is fetched and displayed withAxes.imshow. - Pillow backend (default for custom images, or when client-only
features are requested): the blank is fetched once, then captions are
drawn with
PIL.ImageDrawusing stroke-aware text rendering and a shrink-to-fit loop, and the composed RGBA array isimshown. - Matplotlib backend (legacy, opt-in): captions are drawn with
Axes.textpluspatheffects.Strokefor the classic outlined look. - The bundled Anton font (Impact-like, SIL OFL licensed) is used as a fallback for systems where Impact isn't installed.
Related projects
- matplotlib — the rendering engine.
- memegen — the template registry and blank-image source (api.memegen.link).
- seaborn — the API conventions for
ax=None,**kwargsforwarding, and(fig, ax)returns are modeled on seaborn.
Dependencies
matplotlib >= 3.8numpy,requests,Pillow,platformdirs
Requires Python 3.10+.
License
MIT. The bundled Anton font is licensed under the SIL Open Font License v1.1.
Release files for memeplotlib 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| memeplotlib-0.5.0.tar.gz | 21.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| memeplotlib-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.1 MB
Release files / memeplotlib-0.5.0.tar.gz
| Download URL | memeplotlib-0.5.0.tar.gz |
|---|---|
| Size | 21.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b7cc83cadb05d013fe96315061a1cdcd9893dd84c1e6eab9e27d3458782dc51d
|
|
BLAKE2b-256 checksum How to use checksums |
a0f42d372bd637752f57708cdc729c28f6855a6a4d9cd344750760b458bcb768
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 28, 2026.
Transparency logRelease files / memeplotlib-0.5.0-py3-none-any.whl
| Download URL | memeplotlib-0.5.0-py3-none-any.whl |
|---|---|
| Size | 122.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e9d493a489ef268515ded23619c95ad04c4294df59007424d0b6e141f17ee908
|
|
BLAKE2b-256 checksum How to use checksums |
fba9136d6433887fc2e166a6cc0aa16fb6a1069c4dfde23144725ecefff5b2db
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 28, 2026.
Transparency log