I have a very simple 3D figure constructed with Plotly as lines and disks. No surfaces. No lighting. No complexity. Just lines and disks. Here's a minimal example of this
import numpy as np
import plotly.graph_objects as go
N_DISKS = 8
DISK_POINTS = 60
DISK_RADIUS = 0.4
# Centers along a helix
t = np.arange(N_DISKS)
centers = np.column_stack([
np.cos(t * np.pi / 3) * 1.2,
t * 0.9,
np.sin(t * np.pi / 3) * 1.2,
])
traces = []
lx, ly, lz = [], [], []
for i in range(len(centers) - 1):
lx += [centers[i, 0], centers[i+1, 0], None]
ly += [centers[i, 1], centers[i+1, 1], None]
lz += [centers[i, 2], centers[i+1, 2], None]
traces.append(go.Scatter3d(
x=lx, y=ly, z=lz,
mode="lines+markers",
line=dict(color="black", width=85),
marker=dict(color="black", size=30),
showlegend=False,
))
traces.append(go.Scatter3d(
x=lx, y=ly, z=lz,
mode="lines+markers",
line=dict(color="red", width=65),
marker=dict(color="red", size=24),
showlegend=False,
))
fig = go.Figure(data=traces)
fig.update_layout(
scene=dict(
xaxis=dict(visible=False),
yaxis=dict(visible=False),
zaxis=dict(visible=False),
camera=dict(eye=dict(x=0,y=2.5,z=0)),
aspectmode="cube",
),
margin=dict(l=0, r=0, t=0, b=0),
)
fig.show()

I would like to save this as an SVG. Technically I can do that by calling Plotly.toImage on the JS side as SVG, but the export from this isn't actually an SVG, it's just a PNG wrapped by an SVG. I understand, roughly, why they do this, since they just write to a WebGL context, but it also feels like it is missing the point.
So how do I get real SVG output from this Plotly figure? I understand I can write a different render pipeline that skips Plotly altogether (and I have done this), but surely there is some way to get an actual SVG SVG out of this