Mastering matplotlib.pyplot.figure(): the canvas behind every clear plot

I still remember the first time a plot refused to fit on a slide five minutes before a demo. The data was fine, but the figure size and layout were wrong. That moment taught me that plotting isn’t just about lines and markers—it’s about the canvas that holds them. When you control the canvas, you control the story. If you use Matplotlib regularly, matplotlib.pyplot.figure() is the doorway to that control. It creates the figure object, the blank space where axes, titles, labels, legends, and annotations will live.

In this post I’ll show you how I think about figure() in real work: how I size and configure figures, how I manage multiple figures without confusion, and how I make deliberate decisions about DPI, background color, and layout. You’ll see runnable examples, the small gotchas I see most often in code reviews, and the patterns that keep notebooks clean and predictable. By the end, you should be comfortable using figure() as more than a quick call—you’ll treat it as the backbone of consistent, professional visualizations.

The mental model: figure as the canvas, axes as the frames

When I teach Matplotlib to teams, I use a simple analogy: a figure is the canvas on a desk, and each axes is a frame you place on the canvas. You don’t hang photos in mid-air—you hang them in frames. plt.figure() gives you the canvas. fig.add_subplot() or plt.subplots() gives you the frames.

This matters because many bugs in visualization code come from confusing the two. If you call plt.plot() without an explicit figure or axes, Matplotlib will create them for you. That’s convenient, but it also hides the structure. In short scripts it’s fine; in anything beyond a small notebook cell, I prefer being explicit so I can control size, styling, and layout in predictable ways.

The figure() call can stand alone or be part of a larger pattern. Here are the core parameters you’ll use most often:

  • num: an identifier for the figure, useful when you manage multiple windows.
  • figsize: width and height in inches. This is the single most important parameter for clarity and layout.
  • dpi: dots per inch; determines resolution.
  • facecolor and edgecolor: background and border colors.
  • frameon: whether the figure frame is drawn.
  • clear: whether to clear an existing figure with the same number.

You can pass additional keyword arguments when needed, but those are the essentials I reach for weekly.

Starting with explicit figure creation

Here’s the simplest form I use when I want the figure object directly. I almost always capture it in a variable, even if I don’t need it immediately.

import matplotlib.pyplot as plt

import matplotlib.lines as lines

fig = plt.figure()

# Add simple line artists directly to the figure

fig.add_artist(lines.Line2D([0, 1, 0.5], [0, 1, 0.3]))

fig.add_artist(lines.Line2D([0, 1, 0.5], [1, 0, 0.2]))

plt.title(‘Simple Example of matplotlib.pyplot.figure()‘, fontsize=14, fontweight=‘bold‘)

plt.show()

In this snippet, the figure exists even before any axes are created. I add line artists directly, which is a good demonstration of how low-level Matplotlib can be. In most production scenarios, I create axes and plot into them, but this example makes it clear that the figure is the top-level container.

If you prefer more structure, combine figure() with axes creation:

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(6, 4), dpi=120)

ax = fig.add_subplot(1, 1, 1)

ax.plot([1, 2, 3], [1, 4, 9], marker=‘o‘)

ax.set_title(‘Explicit Figure and Axes‘)

ax.set_xlabel(‘Input Size‘)

ax.set_ylabel(‘Response Time‘)

plt.show()

I like this pattern because you can pass the axes object around in your code or tests. It also keeps plotting predictable when multiple figures exist in the same session.

Size, DPI, and why inches still matter

If there’s one parameter I want you to remember, it’s figsize. The unit is inches, which surprises a lot of people coming from CSS or data dashboards. The actual pixel size is figsize * dpi. That means you can tune physical size and resolution separately.

Here’s how I reason about it:

  • figsize determines layout, spacing, and overall composition.
  • dpi controls sharpness and file size.

If you’re preparing plots for a slide deck, I usually start with something like figsize=(6.5, 3.5) and dpi=150. For a report PDF, I might use dpi=200. For an internal notebook, dpi=100 is often fine.

import matplotlib.pyplot as plt

# A figure sized for a slide with higher DPI

fig = plt.figure(figsize=(6.5, 3.5), dpi=150)

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1, 2, 3], [0, 1, 4, 9], color=‘tab:blue‘)

ax.set_title(‘Slide-friendly Figure‘)

ax.set_xlabel(‘Iteration‘)

ax.set_ylabel(‘Loss‘)

plt.show()

When you combine figsize and dpi deliberately, your plots stop looking cramped or blurry. In my experience, that’s the difference between “quick chart” and “ready for stakeholders.”

A simple sizing heuristic I actually use

When I need a quick, defensible figsize, I use the target container width as my anchor and pick a ratio:

  • A narrow column chart: width 4.5 to 5.5 inches, height 3 to 4 inches.
  • A full-page chart: width 7 to 8.5 inches, height 4.5 to 6 inches.
  • A dashboard tile: width 4 to 6 inches, height 2.5 to 3.5 inches.

I adjust the height until labels feel uncramped and legend placement feels intentional. The key is to pick a target size upfront so layout problems are solved at the root.

Multiple figures without the chaos

As soon as you have more than one figure, implicit state becomes tricky. I’ve seen notebooks where charts overwrite each other or where a later plt.title() affects an earlier figure. The fix is to be explicit and consistent.

import matplotlib.pyplot as plt

# First figure

fig1 = plt.figure(figsize=(6, 4))

ax1 = fig1.add_subplot(1, 1, 1)

ax1.plot([1, 2, 3], [1, 4, 9])

ax1.set_title(‘First Figure‘)

# Second figure

fig2 = plt.figure(figsize=(8, 6))

ax2 = fig2.add_subplot(1, 1, 1)

ax2.plot([1, 2, 3], [9, 4, 1])

ax2.set_title(‘Second Figure‘)

plt.show()

Notice I call plt.show() only once at the end. In notebook environments, this generally renders both figures cleanly. In scripts, it depends on the backend, but the explicit figure objects avoid accidental overlap.

You can also use the num parameter to refer to figures by ID:

import matplotlib.pyplot as plt

fig = plt.figure(num=1, figsize=(5, 3))

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1], [0, 1])

ax.set_title(‘Figure ID 1‘)

# Reusing the same figure number with clear=True resets it

fig = plt.figure(num=1, clear=True, figsize=(5, 3))

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1], [1, 0])

ax.set_title(‘Figure ID 1 (Cleared)‘)

plt.show()

I use num and clear when I have long-running scripts that update plots in stages or when I’m building iterative reporting tools.

When to use figure() vs subplots()

A common question is whether you should use plt.figure() directly or plt.subplots(). I use both, but in different situations.

  • I use figure() when I want full control over the figure creation process, or when I need to manage advanced layouts with addsubplot, addaxes, or manual artists.
  • I use subplots() when I want a quick grid of axes with predictable return values.

Here’s a comparison table I share with teams for clarity:

Traditional vs Modern Approaches

Use case

Traditional pattern

Modern pattern

My recommendation

Single plot

plt.figure(); plt.plot(...)

fig, ax = plt.subplots()

Use subplots() for clean axes handling

Multiple axes with custom placement

fig = plt.figure(); fig.addaxes(...)

fig, ax = plt.subplots() then tweak

Use figure() if placement is custom

Low-level artist control

fig = plt.figure(); fig.addartist(...)

Rare in new code

Use figure()

Quick exploratory plots

plt.plot(...)

fig, ax = plt.subplots()

Use quick stateful calls to keep speedThis isn’t about right or wrong. It’s about context. If you need precision, reach for figure() so you can control it. If you want speed and clarity, subplots() gives you clean axes and a figure in one line.

Real-world patterns: reports, dashboards, and data apps

In 2026, many teams use a mix of notebooks, dashboards, and automated reports. figure() plays a key role in each environment, but the patterns differ.

Static reports

For a PDF report or slide deck, I like deterministic sizing and typography. I set figsize, dpi, and then adjust font sizes on the axes to match the audience.

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(7, 4), dpi=200)

ax = fig.add_subplot(1, 1, 1)

ax.plot([2019, 2020, 2021, 2022, 2023], [1.2, 1.5, 1.8, 2.2, 2.6])

ax.set_title(‘Revenue Growth‘, fontsize=12)

ax.set_xlabel(‘Year‘, fontsize=10)

ax.set_ylabel(‘Revenue (M USD)‘, fontsize=10)

ax.grid(True, alpha=0.3)

plt.tight_layout()

plt.show()

Here I call plt.tight_layout() to reduce clipping. It’s not part of figure() but it’s part of the same story: if you care about presentation, handle layout deliberately.

Dashboards and data apps

When I embed Matplotlib in a web app or an internal dashboard, I still start with figure() but I often render to an in-memory buffer rather than plt.show(). This isn’t about matplotlib itself; it’s about consistent image size in a UI.

import io

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(5, 3), dpi=120)

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1, 2], [0, 1, 4])

ax.set_title(‘Inline Chart‘)

# Render to a bytes buffer (useful for APIs)

buffer = io.BytesIO()

fig.savefig(buffer, format=‘png‘, bbox_inches=‘tight‘)

buffer.seek(0)

This pattern keeps your figure size consistent and avoids layout surprises in UI containers. I often use it with FastAPI or Flask endpoints that return image responses.

AI-assisted workflows

In 2026, many teams generate plots in notebooks with AI support. I’ve found that the fastest way to reduce chart-quality issues is to standardize figure creation. If you use an LLM to draft plotting code, give it a fixed figure template and pass parameters for size and DPI. That keeps output consistent even when the rest of the plot varies.

For example, I keep a small helper function in my toolbox:

import matplotlib.pyplot as plt

def make_figure(width=6, height=4, dpi=120, facecolor=‘white‘):

# Centralized figure creation to keep chart style consistent

fig = plt.figure(figsize=(width, height), dpi=dpi, facecolor=facecolor)

ax = fig.add_subplot(1, 1, 1)

return fig, ax

fig, ax = make_figure()

ax.plot([0, 1, 2], [2, 1, 3])

ax.set_title(‘Standardized Figure Helper‘)

plt.show()

It’s a tiny abstraction, but it helps when multiple people or automated systems generate plots. You keep consistency without micromanaging each chart.

Common mistakes I see in code reviews

I review a lot of Matplotlib code, and the same issues appear repeatedly. These are easy to fix once you know what to look for.

1) Forgetting to store the figure object

When you skip fig = plt.figure(), you lose the ability to manage the figure directly. If you later need to save it or alter facecolor, you’ll have to rely on global state. I recommend capturing it every time you need non-trivial control.

2) Mixing stateful and object-oriented styles

Calling plt.plot() and ax.plot() in the same block usually works, but it makes code harder to reason about. Pick one style. When you use figure(), stick with the object-oriented pattern.

3) Assuming dpi changes figure size

dpi changes resolution, not layout. I’ve seen charts with tiny fonts because someone set dpi=300 and left figsize small. The fix is to adjust figsize for layout and use dpi for clarity.

4) Reusing figure numbers without clearing

If you call plt.figure(num=1) multiple times, you may end up plotting on top of old content. Use clear=True or create a new number to avoid hidden overlays.

5) Hardcoding sizes without context

A 6×4 figure may look fine in a notebook but be too small for a PDF. I recommend basing figsize on the target medium. If you know the width of your report or dashboard column, set figsize accordingly.

When to use figure() and when not to

I’m opinionated here, because clarity matters. If you can follow these guidelines, your plots become easier to maintain.

Use figure() when:

  • You need explicit control of figure size, DPI, or background.
  • You manage multiple figures in a script or notebook.
  • You need to add custom artists or adjust figure-level properties.
  • You save figures to file and want consistent output dimensions.

Avoid figure() when:

  • You only need a quick, exploratory plot in an interactive session.
  • You are using higher-level libraries like Seaborn or Plotly for quick summaries.
  • You prefer the brevity of plt.subplots() for common subplot layouts.

To be clear, there’s nothing wrong with plt.subplots()—I use it often. But if you want the foundation explicit, figure() is the most direct way to do it.

Practical customization beyond the basics

Once you create a figure, you can customize at the figure level for a more polished result. Here are a few patterns I use in production.

Set background and border colors

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(5, 3), facecolor=‘#f7f7f7‘, edgecolor=‘#cccccc‘)

ax = fig.add_subplot(1, 1, 1)

ax.plot([1, 2, 3], [2, 3, 5])

ax.set_title(‘Custom Figure Colors‘)

plt.show()

This is useful when placing figures on non-white backgrounds or when you want a subtle visual boundary in reports.

Use frameon for clean exports

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(5, 3), frameon=False)

ax = fig.add_subplot(1, 1, 1)

ax.plot([1, 2, 3], [1, 4, 9])

ax.set_title(‘Frame Disabled‘)

plt.show()

When I export charts for dashboards, I often turn off the frame for a cleaner overlay.

Use clear=True for iterative updates

import matplotlib.pyplot as plt

# First render

fig = plt.figure(num=2, figsize=(4, 3))

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1], [0, 1])

ax.set_title(‘Initial‘)

# Update the same figure

fig = plt.figure(num=2, clear=True, figsize=(4, 3))

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1], [1, 0])

ax.set_title(‘Updated‘)

plt.show()

This is a great technique for scripts that update figures in a loop or re-render charts with new data.

Performance considerations and practical ranges

Performance in Matplotlib is usually more about how much data you draw than how you create figures. Still, figure settings affect rendering time and file size. In my experience:

  • Higher DPI increases render time and image size. For most reports, dpi=150 to dpi=200 is a good balance.
  • Very large figsize makes layout slower and can cause huge files. I keep width under 10 inches unless there’s a specific need.
  • The biggest time cost is plotting large datasets; consider downsampling or using line simplification if you plot millions of points.

I don’t chase micro-benchmarks here. Instead, I focus on predictable ranges. If you keep figure size reasonable and DPI moderate, rendering time typically stays in a smooth, interactive range for common datasets.

Debugging layout issues with a repeatable checklist

When a plot looks wrong, I run a quick checklist. It saves time and prevents me from “fixing” the wrong thing.

1) Check the figure size first. If figsize is too small, everything else is a downstream problem.

2) Check font sizes relative to the figure. If fonts are large, they can push labels out of view.

3) Inspect tightlayout() or constrainedlayout. These help with clipping, but can also squeeze plots if the figure is too small.

4) Confirm the backend and display size. Notebooks can scale plots differently; what looks fine inline may clip when saved.

5) Use bbox_inches=‘tight‘ on save. This is often the final fix for a clipped title or legend.

If I’m stuck, I print the figure size and DPI and do the math. Knowing the pixel dimensions makes the issue concrete and easier to fix.

Layout control: beyond tight_layout

Figure creation is only step one. The real control comes from layout. There are three main tools I use, and it helps to know when each is appropriate.

  • plt.tight_layout(): quick fix for basic plots; good default, sometimes too aggressive.
  • constrained_layout=True on figure(): better for complex layouts and subplots; more stable with colorbars and suptitles.
  • fig.add_axes() for manual placement: maximum control, minimal automation.

Here’s a side-by-side example using constrained_layout:

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(7, 3.5), dpi=120, constrained_layout=True)

ax1 = fig.add_subplot(1, 2, 1)

ax2 = fig.add_subplot(1, 2, 2)

ax1.plot([1, 2, 3], [1, 4, 2])

ax1.set_title(‘Left‘)

ax1.set_xlabel(‘X‘)

ax1.set_ylabel(‘Y‘)

ax2.plot([1, 2, 3], [2, 1, 3])

ax2.set_title(‘Right‘)

ax2.set_xlabel(‘X‘)

plt.show()

I use constrainedlayout when I know the figure will have multiple axes, legends, or colorbars. It’s not perfect, but it’s more consistent than tightlayout() for complex arrangements.

Manual placement when you need editorial control

Sometimes you need two charts and a text box or a legend area in a fixed spot. That’s when I reach for add_axes, which takes a normalized rectangle [left, bottom, width, height].

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(8, 4))

main = fig.add_axes([0.08, 0.2, 0.6, 0.7])

side = fig.add_axes([0.72, 0.2, 0.25, 0.7])

main.plot([1, 2, 3], [1, 4, 9])

main.set_title(‘Main Chart‘)

main.set_xlabel(‘Input‘)

main.set_ylabel(‘Output‘)

side.bar([‘A‘, ‘B‘, ‘C‘], [3, 5, 2])

side.set_title(‘Breakdown‘)

plt.show()

This is less automatic, but it gives you exact control. For data storytelling, this is often worth it.

Figure-level text: suptitle, annotations, and branding

Most people focus on axes titles and labels, but figure-level elements can add clarity and consistency.

suptitle for multi-panel context

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(7, 4), dpi=120)

fig.suptitle(‘Customer Cohorts: Retention Over Time‘, fontsize=12, fontweight=‘bold‘)

ax1 = fig.add_subplot(1, 2, 1)

ax2 = fig.add_subplot(1, 2, 2)

ax1.plot([1, 2, 3, 4], [100, 80, 60, 55])

ax1.set_title(‘Cohort A‘)

ax2.plot([1, 2, 3, 4], [100, 70, 50, 40])

ax2.set_title(‘Cohort B‘)

plt.tight_layout()

plt.show()

When I use suptitle, I often need extra top margin. tightlayout() can push it; sometimes I set rect or use constrainedlayout for better results.

Adding small branding or context labels

In reports, I sometimes add a small label like “Internal” or a data source at the figure level. It keeps charts self-contained.

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(6, 3.5))

ax = fig.add_subplot(1, 1, 1)

ax.plot([1, 2, 3], [1, 2, 3])

fig.text(0.99, 0.01, ‘Source: internal data‘, ha=‘right‘, va=‘bottom‘, fontsize=8, color=‘#555555‘)

fig.text(0.01, 0.99, ‘Confidential‘, ha=‘left‘, va=‘top‘, fontsize=8, color=‘#555555‘)

plt.show()

These figure-level texts are subtle but effective for reusable charts.

Saving figures: what I wish more people knew

Saving is where figure configuration actually becomes visible to your audience. A chart that looks fine inline can look wrong when saved if you don’t manage the figure properly.

Use savefig with explicit size and bbox

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(6.5, 3.5), dpi=150)

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1, 2], [0, 1, 4])

ax.set_title(‘Saved Figure Example‘)

fig.savefig(‘reportchart.png‘, dpi=150, bboxinches=‘tight‘)

bbox_inches=‘tight‘ trims extra whitespace and usually fixes clipped labels. If you use a transparent background, set facecolor=‘none‘ or transparent=True during savefig.

PNG vs SVG vs PDF in practice

  • PNG: best for dashboards and presentations, supports transparency.
  • SVG: best for web and vector editing; file size can grow with many points.
  • PDF: best for reports, high-quality print output.

If I’m exporting for a report, I usually generate both a PNG (for quick review) and a PDF (for final use). The figure settings carry over.

Edge cases that trip people up

I want to call out a few tricky scenarios where figure() settings matter more than usual.

Long labels or rotated ticks

When labels are long, figsize has to grow or the labels must rotate. Otherwise they will clip. I prefer to increase width first, then rotate if needed.

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(8, 3.5))

ax = fig.add_subplot(1, 1, 1)

ax.bar([‘Very Long Category A‘, ‘Very Long Category B‘, ‘Very Long Category C‘], [3, 5, 2])

ax.set_title(‘Long Labels‘)

ax.tick_params(axis=‘x‘, rotation=30)

plt.tight_layout()

plt.show()

Colorbars in multi-axes layouts

Colorbars consume space and can distort your layout. constrainedlayout handles this better than tightlayout.

import matplotlib.pyplot as plt

import numpy as np

data = np.random.rand(10, 10)

fig = plt.figure(figsize=(5, 4), constrained_layout=True)

ax = fig.add_subplot(1, 1, 1)

im = ax.imshow(data, cmap=‘viridis‘)

fig.colorbar(im, ax=ax)

plt.show()

Transparent backgrounds and facecolor mismatch

If you set facecolor on the figure but save with transparent=True, you’ll end up with a transparent output. That’s not wrong, but it surprises people. Decide whether you want the figure to have a solid background in the exported file, and set it explicitly.

Figure templates with rcParams

If you make lots of plots, it’s worth standardizing figure creation via rcParams. Think of it as a style profile for your charts. I use it to set defaults for figure.figsize, figure.dpi, and font sizes. Then plt.figure() just works without repeating parameters.

import matplotlib as mpl

import matplotlib.pyplot as plt

mpl.rcParams.update({

‘figure.figsize‘: (6, 4),

‘figure.dpi‘: 120,

‘font.size‘: 10,

‘axes.titlesize‘: 11,

‘axes.labelsize‘: 10,

})

fig = plt.figure()

ax = fig.add_subplot(1, 1, 1)

ax.plot([1, 2, 3], [1, 4, 2])

ax.set_title(‘Defaults from rcParams‘)

plt.show()

This reduces boilerplate and makes team output more consistent. In larger projects, I keep these defaults in a small module and import it at the top of notebooks.

Reproducibility and clean notebooks

One of the biggest frustrations in notebooks is visual drift: a chart that looks different today than it did last week. figure() helps you lock things down if you’re disciplined.

Here’s my minimal reproducibility checklist:

  • Set figsize and dpi explicitly for any plot you plan to share.
  • Avoid relying on global state from previous cells.
  • Close figures with plt.close(fig) if you generate many in a loop.

Example of closing figures to avoid memory issues:

import matplotlib.pyplot as plt

for i in range(3):

fig = plt.figure(figsize=(5, 3))

ax = fig.add_subplot(1, 1, 1)

ax.plot([0, 1, 2], [i, i + 1, i + 2])

ax.set_title(f‘Iteration {i}‘)

plt.show()

plt.close(fig)

This keeps notebook memory under control and avoids “ghost” figures that appear later.

Testing plots without eyeballing them

This is subtle, but important for production pipelines. If you generate plots programmatically, you can test their size and properties without visual inspection.

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(6, 4), dpi=100)

width, height = fig.getsizeinches()

dpi = fig.get_dpi()

assert width == 6

assert height == 4

assert dpi == 100

You can also check that a title exists or that a legend is present. It’s not glamorous, but it catches regressions early.

Practical scenarios and patterns I rely on

Scenario 1: A two-chart report with shared styling

I want two plots, same size, same style, saved as PNGs for a PDF report.

import matplotlib.pyplot as plt

def report_fig():

fig = plt.figure(figsize=(6.5, 3.5), dpi=150)

ax = fig.add_subplot(1, 1, 1)

ax.grid(True, alpha=0.3)

return fig, ax

fig1, ax1 = report_fig()

ax1.plot([1, 2, 3], [1, 4, 9])

ax1.set_title(‘Metric A‘)

fig1.savefig(‘metrica.png‘, bboxinches=‘tight‘)

fig2, ax2 = report_fig()

ax2.plot([1, 2, 3], [9, 4, 1])

ax2.set_title(‘Metric B‘)

fig2.savefig(‘metricb.png‘, bboxinches=‘tight‘)

Here figure() is hidden behind a helper, but it still defines the backbone.

Scenario 2: A figure with a custom legend panel

If I want a legend to be a separate panel, I use add_axes.

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(7, 4))

ax = fig.add_axes([0.08, 0.1, 0.6, 0.8])

legax = fig.addaxes([0.72, 0.1, 0.25, 0.8])

line1, = ax.plot([1, 2, 3], [1, 2, 3], label=‘Series A‘)

line2, = ax.plot([1, 2, 3], [3, 2, 1], label=‘Series B‘)

leg_ax.axis(‘off‘)

leg_ax.legend([line1, line2], [‘Series A‘, ‘Series B‘], loc=‘center‘)

plt.show()

This keeps the main plot clean and gives me control over label presentation.

Scenario 3: Consistent figure sizes in a dashboard grid

For dashboards, you want consistent sizes so every tile aligns visually.

import matplotlib.pyplot as plt

def dashboard_tile(width=4.5, height=2.8):

fig = plt.figure(figsize=(width, height), dpi=120)

ax = fig.add_subplot(1, 1, 1)

return fig, ax

fig, ax = dashboard_tile()

ax.plot([0, 1, 2], [0, 1, 4])

ax.set_title(‘Tile Example‘)

fig.savefig(‘tile.png‘, bbox_inches=‘tight‘)

The key is that figure creation is consistent, which makes the dashboard feel coherent.

Alternative approaches and when I choose them

Matplotlib isn’t the only tool, and sometimes figure() is not the right abstraction. I still reach for it when:

  • I need granular control for publishing or reports.
  • I care about exact export size and layout.
  • I’m integrating plots into other systems.

I choose alternatives when:

  • I need interactive charts for the web (I use Plotly or other tools).
  • I need fast, high-level statistical visualizations (I use Seaborn, but still consider figure() for layout control).
  • I need dashboards with live interactivity (I use web-native charting).

Even then, understanding figure() helps, because many libraries still sit on top of Matplotlib or follow its design concepts.

A quick reference: figure() parameters I actually use

To close, here’s the short list I keep in my head. If you memorize this, you’ll be comfortable in 90 percent of real-world cases.

  • figsize: define layout and composition.
  • dpi: define resolution.
  • facecolor: set background explicitly when needed.
  • frameon: toggle frame for cleaner exports.
  • num and clear: manage multiple figures or iterative updates.
  • constrained_layout: enable for complex layouts.

If you internalize these, figure() stops being a boilerplate call and becomes your layout toolkit.

Final thoughts: the canvas is the story

When people say Matplotlib charts look “old,” it’s rarely about the data or the plotting calls. It’s almost always about the canvas: poor sizing, cramped labels, or inconsistent layout. matplotlib.pyplot.figure() is the place where you fix that. It’s the first step, but it’s also the quiet foundation that supports everything else.

I treat figure() the way a designer treats a layout grid. Get the canvas right and everything else becomes easier. If you adopt that mindset, your plots will stop feeling like quick throwaways and start looking like deliberate communication. That’s the difference between “I plotted my data” and “I told the story clearly.”

Scroll to Top