marimo icon indicating copy to clipboard operation
marimo copied to clipboard

Using plt.show with plots that depend on input values causes severe flickering

Open FeldrinH opened this issue 9 months ago • 8 comments

Describe the bug

Using plt.show() to show matplotlib plots works fine for static plots. However, it causes extreme flickering when the plot is redrawn every time an input value (e.g. slider value) changes:

https://github.com/user-attachments/assets/e0e729a8-c1cc-407f-8028-0c9fc56e4910

Environment

{
  "marimo": "0.11.25",
  "OS": "Windows",
  "OS Version": "10",
  "Processor": "Intel64 Family 6 Model 183 Stepping 1, GenuineIntel",
  "Python Version": "3.12.6",
  "Binaries": {
    "Browser": "134.0.6998.117",
    "Node": "v20.12.2"
  },
  "Dependencies": {
    "click": "8.1.7",
    "docutils": "0.21.2",
    "itsdangerous": "2.2.0",
    "jedi": "0.19.1",
    "markdown": "3.7",
    "narwhals": "1.31.0",
    "packaging": "24.1",
    "psutil": "6.0.0",
    "pygments": "2.18.0",
    "pymdown-extensions": "10.12",
    "pyyaml": "6.0.2",
    "ruff": "0.11.2",
    "starlette": "0.45.3",
    "tomlkit": "0.13.2",
    "typing-extensions": "4.12.2",
    "uvicorn": "0.34.0",
    "websockets": "15.0"
  },
  "Optional Dependencies": {
    "pandas": "2.2.3"
  },
  "Experimental Flags": {}
}

Code to reproduce

import marimo

__generated_with = "0.11.25"
app = marimo.App(width="medium")


@app.cell
def _():
    import marimo as mo
    return (mo,)


@app.cell
def _(mo):
    v = mo.ui.slider(start=6.28, stop=62.8, step=0.1, show_value=True)
    v
    return (v,)


@app.cell
def _(v):
    import numpy as np
    import matplotlib.pyplot as plt

    x = np.arange(0, 1, 0.001)
    y = np.sin(x * v.value)
    plt.plot(x, y)
    plt.show()
    return np, plt, x, y


if __name__ == "__main__":
    app.run()

FeldrinH avatar Mar 23 '25 00:03 FeldrinH

You need to .gca() instead of .show(). The latter prints to the console output which gets cleared on each run (causing the flicker). The former is sent to the cell output and the recommended way to use matplotlib in marimo

mscolnick avatar Mar 23 '25 00:03 mscolnick

I see. I understand why it works this way on a technical level, but as a user this seems like bad UX. No other environment functions like this and the docs even explicitly advertise that Marimo works like other environments:

Just import your plotting library of choice and use it as you normally would.

(from https://docs.marimo.io/guides/working_with_data/plotting/)

PS: I know the docs mention using plt.gca() instead of plt.show(), but that is an easy detail to miss when you are just skimming and the page starts with a clear claim that plotting libraries function the same as in any other environment.

FeldrinH avatar Mar 23 '25 15:03 FeldrinH

Yea, we can look into debouncing the clearing of the console output to give new output a change to replace it without a flicker

mscolnick avatar Mar 23 '25 15:03 mscolnick

Hmm, I'm also seing bad flickering with plt.gca() when using mo.mpl.interactive. The window jumps up and down while the div for the interactive plot appears and disappears.

import marimo

__generated_with = "0.11.25"
app = marimo.App(width="medium")


@app.cell
def _():
    import marimo as mo
    return (mo,)


@app.cell
def _(mo):
    v = mo.ui.slider(start=6.28, stop=62.8, step=0.1, show_value=True)
    v
    return (v,)


@app.cell
def _(v):
    import numpy as np
    import matplotlib.pyplot as plt

    x = np.arange(0, 1, 0.001)
    y = np.sin(x * v.value)
    plt.plot(x, y)
    mo.mpl.interactive(plt.gca())
    return np, plt, x, y


if __name__ == "__main__":
    app.run()

marcelroed avatar May 19 '25 19:05 marcelroed

I found a fix for my issue which is way faster. I have matplotlib output SVGs and show them in HTML elements:

def show_svg():
    import io
    f = io.BytesIO()
    plt.savefig(f, format = "svg")
    svg_str = f.getvalue()
    return mo.Html(svg_str.decode('utf-8'))

Then I added sliders and such to replace the interactive mode.

marcelroed avatar May 26 '25 23:05 marcelroed

@marcelroed this is neat! StringIO works too, no need for decode.

This gives you access to all the extra styling; you will often need bbox_inches="tight":

savefig(fname, *, transparent=None, dpi='figure', format=None,
        metadata=None, bbox_inches=None, pad_inches=0.1,
        facecolor='auto', edgecolor='auto', backend=None,
        **kwargs
       )

Image

liquidcarbon avatar Nov 13 '25 19:11 liquidcarbon

Normally it takes ~100 ms to solve some ODEs and draw these plots. Then I leave the notebook alone and after a while marimo gets very very slow, 5-10 seconds to respond to slider change. VPN reconnection may or may not be involved. Restarting kernel helps.

@mscolnick any tips on diagnosis / prevention? Nothing helpful in browser console or marimo logs (have not tried debug).

liquidcarbon avatar Nov 13 '25 19:11 liquidcarbon

@liquidcarbon im not sure i have any ideas without seeing code or playing around with it myself.

mscolnick avatar Nov 14 '25 01:11 mscolnick