marimo
marimo copied to clipboard
Altair charts with width property are turned into bitmaps
Describe the bug
It is valuable to be able to set the width and height of an Altair chart if you want to have particular aspect ratios or export bitmaps, svgs, PDFs.
When Altair charts are specified with the width property e.g.,
.properties(
width=500
)
they become rendered as bitmaps rather than vector graphics. E.g.,
vs. this where the only difference is the width and height properties being specified:
(The first is just a bitmap screencap of the vector graphic for this issue.)
The problem occurs whether you specify the width with .properties or in the call to alt.chart. E.g., alt.Chart(cars, width=500)
Surprisingly, it doesn't seem to be a problem if just height is specified!
Using alt.renderers.enable('svg') fixes some issues, but adds others.
Note that I had to restart the kernel after every change as there seemed to be some issue with cell rendering not being updated? I'm unclear on that.
Environment
{
"marimo": "0.12.3",
"OS": "Windows",
"OS Version": "11",
"Processor": "Intel64 Family 6 Model 165 Stepping 5, GenuineIntel",
"Python Version": "3.11.11",
"Binaries": {
"Browser": "129.0.6668.90",
"Node": "v22.9.0"
},
"Dependencies": {
"click": "8.1.8",
"docutils": "0.21.2",
"itsdangerous": "2.2.0",
"jedi": "0.19.2",
"markdown": "3.7",
"narwhals": "1.33.0",
"packaging": "24.2",
"psutil": "7.0.0",
"pygments": "2.19.1",
"pymdown-extensions": "10.14.3",
"pyyaml": "6.0.2",
"ruff": "0.11.3",
"starlette": "0.46.1",
"tomlkit": "0.13.2",
"typing-extensions": "4.13.1",
"uvicorn": "0.34.0",
"websockets": "15.0.1"
},
"Optional Dependencies": {
"altair": "5.5.0",
"anywidget": "0.9.18",
"pandas": "2.2.3",
"pyarrow": "19.0.1"
},
"Experimental Flags": {}
}
Code to reproduce
See the WASM version: https://marimo.app/l/2hyx0v
Here is an alternate version exploring what happens when you add alt.renderers.enable('svg'): https://marimo.app/l/bwmiw1
(though this doesn't work in WSAM due to missing a wheel needed for Altair SVG export... It works locally.)
The code for the former is here:
import marimo
__generated_with = "0.11.31-dev3"
app = marimo.App()
@app.cell
def _():
import marimo as mo
import altair as alt
import vega_datasets
cars = vega_datasets.data.cars()
return alt, cars, mo, vega_datasets
@app.cell
def _(alt, cars):
# The normal chart works fine
chart = alt.Chart(cars).mark_point().encode(
x='Horsepower',
y='Miles_per_Gallon',
color='Origin',
)
chart
return (chart,)
@app.cell
def _(chart):
# Adding width and height makes everything bitmap
chartSized = chart.properties(
width=500,
height=100
)
chartSized
return (chartSized,)
@app.cell
def _(chartSized, mo):
# Wrapping it doesn't work
mo.ui.altair_chart(chartSized)
return
@app.cell
def _(chartSized, mo):
# Stacks don't work either
mo.vstack([
chartSized
])
return
if __name__ == "__main__":
app.run()
I'm not sure I totally understand. Is this an issue on marimo?
Can you reproduce this in the vega playground?
I don't see the problem using the vega editor. Here are examples. After loading them in the vega editor, I changed the settings to show SVG and they all worked fine. This seems to be a rendering issue in Marimo, rather than Altair or Vega.
Unsized chart from Jupyterlab Altair
Sized chart from JupyterLab Altair
At least per https://jsondiff.com/, the only difference is the latter has the width and height properties.
If I make with Marimo
Unsized chart in Marimo Altair
Sized chart with Marimo Altair
The difference between these two is that the unsized has "width": "container", while the sized has "height": 100, "width": 500.
The difference between the Jupyterlab and Marimo generated unsized files is that Marimo adds
"usermeta": {
"embedOptions": {
}
},
"width": "container"
I'm still unclear what the marimo issue is. We do default to width "container" because it looks better and more responsive.
But at the end of the day, is anything wrong with the spec? Or is the spec ok, but just rendered wrong when in marimo?
We then pass this spec to the Vega library for rendering, without doing anything fancy.
I think the spec is fine, at least as rendered by vega on their editor and Altair in a jupyter notebook. My best guess is that somehow the rendering in Marimo is different and it is introducing the bitmap problem.
Thanks for debugging. We don't do anything with the spec (unless wrapped in mo.ui.altair()).
We just pass it directly to react-vega which uses the vega library underneath to render the spec.
It could be a version issue with the vega spec or an upstream issue on Vega.
Also I am not sure i can reproduce what you are seeing, even from the example and playground you linked. How can you tell what is Bitmap vs Vector Graphics?
You're welcome! Thanks for all your work on this.
You should be able to see the issue in the WASM version I uploaded. If you use the browser zoom on the first two images, you should see that the top stays nice and crisp while the second gets fuzzy. The same thing happens when I launch marimo locally.
However, the problem doesn't show up in the Vega editor with any of the specs.
Oh wow, yes i see that now (by zooming in). That's a much better indicator for me - and will help me debug this.
The "width": "container" default explains something I ran into.
Specifically, some compounds charts are tuned with configure_view:
chart.configure_view(
continuousWidth=200,
)
normally it's equivalent to chart.properties(width=200), but it takes no effect when main width property is set to "container".