# Plotly: huge html file size

**URL:** <https://community.plotly.com/t/plotly-huge-html-file-size/64342>\
**Category:** 📊 Plotly Python\
**Created:** [May 21, 2022, 1:55am UTC](https://community.plotly.com/t/plotly-huge-html-file-size/64342 "2022-05-21T01:55:39Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![jerry2021](https://avatars.discourse-cdn.com/v4/letter/j/ecc23a/32.png) [@jerry2021](https://community.plotly.com/u/jerry2021)\
**Post date:** [May 21, 2022, 1:55am UTC](https://community.plotly.com/t/plotly-huge-html-file-size/64342/1 "2022-05-21T01:55:40Z")

</div>

I have a 3D bin packing model which uses plotly to draw the output graph. I noticed that with 600+items being ploted, it takes a long time to generate the html file and the file size is 89M, which is crazy (I doubt there might be some huge duplications). why does it make such a big file? How to control the size to an acceptable level (no more than 5M as I need to render it in my website). many thanks for the help.

 ![image](https://us1.discourse-cdn.com/flex024/uploads/plot/original/3X/d/9/d955f767173e41ce321c94ce6e0a94eaa71e3400.png)

below is my full code (please skip the model code and see from the plotly code)

> from py3dbp import Packer, Bin, Item, Painter  
> import time  
> import plotly.graph\_objects as go  
> from plotly.subplots import make\_subplots  
> import plotly  
> import pandas as pd
> 
> start = time.time()  
> import numpy as np
> 
> # -----------this part is about calculating the 3D bin packing problem to get x,y,z for each items of a bin/container--------------
> 
> ###library reference: [GitHub - jerry800416/3D-bin-packing: 3D Bin Packing improvements based on https://github.com/enzoruiz/3dbinpacking](https://github.com/jerry800416/3D-bin-packing)
> 
> # init packing function
> 
> packer = Packer()
> 
> # init bin
> 
> # box = Bin(‘40HC-1’, (1203, 235, 259), 18000.0,0,0)
> 
> box = Bin(‘40HC-1’, (1202.4, 235, 269.7), 18000.0, 0, 0)  
> packer.addBin(box)
> 
> # add item
> 
> # for num in range(10):
> 
> # packer.addItem(Item(f"BoxA\_{num}“, f"BoxA\_{num}”, ‘cube’, (120, 120, 120), 8.20, 1, 100, True, ‘red’))
> 
> # for num in range(55):
> 
> # packer.addItem(Item(f"BoxB\_{num}“, f"BoxB\_{num}”, ‘cube’, (65, 38, 90), 14, 1, 100, True, ‘blue’))
> 
> # for num in range(50):
> 
> # packer.addItem(Item(f"BoxC\_{num}“, f"BoxC\_{num}”, ‘cube’, (143, 52, 47), 10, 1, 100, True, ‘gray’))
> 
> # add item
> 
> # for num in range(12):
> 
> # packer.addItem(Item(f"BoxA\_{num}“, f"BoxA\_{num}”, ‘cylinder’, (120, 120, 120), 8.20, 1, 100, True, ‘red’))
> 
> # for num in range(120):
> 
> # packer.addItem(Item(f"BoxB\_{num}“, f"BoxB\_{num}”, ‘cube’, (65, 38, 90), 14, 1, 100, True, ‘blue’))
> 
> # for num in range(60):
> 
> # packer.addItem(Item(f"BoxC\_{num}“, f"BoxC\_{num}”, ‘cube’, (143, 52, 47), 10, 1, 100, True, ‘gray’))
> 
> # for num in range(12):
> 
> # packer.addItem(Item(f"BoxA\_{num}“, f"BoxA\_{num}”, ‘cylinder’, (120, 120, 120), 8.20, 1, 100, True, ‘red’))
> 
> # for num in range(33):
> 
> # packer.addItem(Item(f"BoxB\_{num}“, f"BoxB\_{num}”, ‘cube’, (65, 38, 90), 14, 1, 100, True, ‘blue’))
> 
> # for num in range(32):
> 
> # packer.addItem(Item(f"BoxC\_{num}“, f"BoxC\_{num}”, ‘cube’, (143, 52, 47), 10, 1, 100, True, ‘gray’))
> 
> for num in range(252):  
> packer.addItem(Item(f"BoxA\_{num}“, f"BoxA\_{num}”, ‘cube’, (65, 33, 26), 2.06, 1, 100, True, ‘red’))  
> for num in range(222):  
> packer.addItem(Item(f"BoxB\_{num}“, f"BoxB\_{num}”, ‘cube’, (84, 42.5, 33), 2.72, 1, 100, True, ‘blue’))  
> for num in range(270):  
> packer.addItem(Item(f"BoxC\_{num}“, f"BoxC\_{num}”, ‘cube’, (48, 48, 38), 2.17, 1, 100, True, ‘gray’))
> 
> # calculate packing
> 
> packer.pack(bigger\_first=True, distribute\_items=False, fix\_point=True, number\_of\_decimals=0)
> 
> # print result
> 
> b = packer.bins[0]  
> volume = b.width \* b.height \* b.depth  
> print(“:::::::::::”, b.string())
> 
> print(“FITTED ITEMS:”)  
> volume\_t = 0  
> volume\_f = 0  
> unfitted\_name = ‘’  
> for item in b.items:  
> print("partno : ", item.partno)  
> print("color : ", item.color)  
> print("position : ", item.position)  
> print("type of : ", item.typeof)  
> print("rotation type : ", item.rotation\_type)  
> print("W_H_D : ", str(item.width) + ‘_’ + str(item.height) + '_’ + str(item.depth))  
> print("volume : ", float(item.width) \* float(item.height) \* float(item.depth))  
> print(“weight : “, float(item.weight))  
> volume\_t += float(item.width) \* float(item.height) \* float(item.depth)  
> print(”_**“)  
> print(”**_”)  
> print(“UNFITTED ITEMS:”)  
> for item in b.unfitted\_items:  
> print("partno : ", item.partno)  
> print("color : ", item.color)  
> print("W_H_D : ", str(item.width) + ‘_’ + str(item.height) + '_’ + str(item.depth))  
> print("volume : ", float(item.width) \* float(item.height) \* float(item.depth))  
> print(“weight : “, float(item.weight))  
> volume\_f += float(item.width) \* float(item.height) \* float(item.depth)  
> unfitted\_name += ‘{},’.format(item.partno)  
> print(”_**“)  
> print(”**_”)  
> print(‘space utilization : {}%’.format(round(volume\_t / float(volume) \* 100, 2)))  
> print('residual volumn : ', float(volume) - volume\_t)  
> print('unpack item : ', unfitted\_name)  
> print('unpack item volumn : ', volume\_f)  
> print("gravity distribution : ", b.gravity)  
> stop = time.time()  
> print('used time : ', stop - start)
> 
> # draw results
> 
> # painter = Painter(b)
> 
> # painter.plotBoxAndItems()
> 
> # ----------------------------------end---------------------------------------------
> 
> ############################### PLOTLY ############################################
> 
> # [3d mesh plots in Python](https://plotly.com/python/3d-mesh/#mesh-cube)
> 
> def vertices(xmin=0, ymin=0, zmin=0, xmax=1, ymax=1, zmax=1):  
> return {  
> “x”: [xmin, xmin, xmax, xmax, xmin, xmin, xmax, xmax],  
> “y”: [ymin, ymax, ymax, ymin, ymin, ymax, ymax, ymin],  
> “z”: [zmin, zmin, zmin, zmin, zmax, zmax, zmax, zmax],  
> “i”: [7, 0, 0, 0, 4, 4, 6, 1, 4, 0, 3, 6],  
> “j”: [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],  
> “k”: [0, 7, 2, 3, 6, 7, 1, 6, 5, 5, 7, 2],  
> }
> 
> def parallelipipedic\_frame(xm, xM, ym, yM, zm, zM):
> 
> # defines the coords of each segment followed by None, if the line is
> 
> # discontinuous
> 
> x = [xm, xM, xM, xm, xm, None, xm, xM, xM, xm, xm, None, xm, xm, None, xM, xM,  
> None, xM, xM, None, xm, xm]  
> y = [ym, ym, yM, yM, ym, None, ym, ym, yM, yM, ym, None, ym, ym, None, ym, ym,  
> None, yM, yM, None, yM, yM]  
> z = [zm, zm, zm, zm, zm, None, zM, zM, zM, zM, zM, None, zm, zM, None, zm, zM,  
> None, zm, zM, None, zm, zM]  
> return x, y, z
> 
> def slice\_triangles(z, n, i, j, k, l):  
> “”“Create the triangles of a single slice”“”  
> return [[z, j, i], [i, j, l], [l, j, k], [k, n, l]]
> 
> def cylinder\_mesh(r, xs, ys, zs, h, n\_slices=40):  
> “”“Create a cylindrical mesh”“”  
> theta = np.linspace(0, 2 \* np.pi, n\_slices + 1)  
> x = xs + r \* np.cos(theta)  
> y = ys + r \* np.sin(theta)  
> z1 = zs + 0 \* np.ones\_like(x)  
> z2 = (zs + h) \* np.ones\_like(x)
> 
> # index of the final point in the mesh
> 
> n = n\_slices \* 2 + 1
> 
> # build triangulation
> 
> triangles =   
> for s in range(1, n\_slices + 1):  
> j = (s + 1) if (s \<= n\_slices - 1) else 1  
> k = j + n\_slices if (s \<= n\_slices - 1) else n\_slices + 1  
> l = s + n\_slices  
> triangles += slice\_triangles(0, n, s, j, k, l)  
> triangles = np.array(triangles)
> 
> # coordinates of the vertices
> 
> x\_coords = np.hstack([xs, x[:-1], x[:-1], xs])  
> y\_coords = np.hstack([ys, y[:-1], y[:-1], ys])  
> z\_coords = np.hstack([zs, z1[:-1], z2[:-1], (zs + h)])  
> vertices = np.stack([x\_coords, y\_coords, z\_coords]).T
> 
> return vertices, triangles, x, y, z1, z2
> 
> # def cylinder\_traces(r, xs, ys, zs, h, n\_slices=40, show\_mesh=True, n\_sub=4, surface\_kw={}, line\_kw={}):
> 
> def cylinder\_traces(r, xs, ys, zs, h, color, name, n\_slices=40, show\_mesh=True, n\_sub=4, line\_kw={}):  
> “”"  
> r : radius  
> xs, ys, zs : start position of the cylinder  
> h : height of the cylinder  
> n\_slices : number of slices in the circumferential direction  
> show\_mesh : whether to display pseudo-wireframe  
> n\_sub : number of subdivision in along the height for the pseudo-wireframe  
> surface\_kw : customize the appearance of the surface  
> line\_kw : customize the appearance of the wireframe  
> “”"  
> vertices, triangles, x, y, z1, z2 = cylinder\_mesh(r, xs, ys, zs, h, n\_slices)
> 
> # surface = go.Mesh3d(
> 
> # x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2],
> 
> # i=triangles[:, 0], j=triangles[:, 1], k=triangles[:, 2],
> 
> # \*\*surface\_kw)
> 
> # print("box\_id: ", name)
> 
> surface = go.Mesh3d(  
> x=vertices[:, 0], y=vertices[:, 1], z=vertices[:, 2],  
> i=triangles[:, 0], j=triangles[:, 1], k=triangles[:, 2],  
> color=color, name=name)
> 
> traces = [surface]  
> if not show\_mesh:  
> return traces
> 
> line\_kw.setdefault(“showlegend”, False)
> 
> # horizontal mesh lines
> 
> zsubs = np.linspace(zs, zs + h, n\_sub + 1)  
> for zc in zsubs:  
> traces.append(go.Scatter3d(x=x, y=y, z=zc \* np.ones\_like(x), mode=“lines”,name=name, \*\*line\_kw))
> 
> # vertical mesh lines
> 
> for \_x, \_y in zip(x, y):  
> traces.append(go.Scatter3d(x=[\_x, \_x], y=[\_y, \_y], z=[zs, zs + h], mode=“lines”, name=name, \*\*line\_kw))  
> # print("traces: ", traces)  
> return traces
> 
> # take a packer item and build parameters to a plotly mesh3d cube
> 
> def packer\_to\_plotly(item):  
> colors = [“crimson”, “limegreen”, “green”, “red”, “cyan”, “magenta”, “yellow”]  
> ret = vertices(  
> \*item.position, \*[sum(x) for x in zip(item.position, item.getDimension())]  
> )  
> ret[“name”] = item.name  
> ret[“color”] = colors[ord(item.name.split(“\_”)[0][-1]) - ord(“A”)]  
> return ret
> 
> # create a figure for each bin
> 
> fig = go.Figure()
> 
> # add a trace for each packer item
> 
> for row, pbin in enumerate(packer.bins):  
> for item in pbin.items:  
> fig.add\_trace(go.Mesh3d(packer\_to\_plotly(item)))
> 
> # some first attempts at sorting out layout, prmarily aspect ratio
> 
> fig.update\_layout(  
> margin={“l”: 0, “r”: 0, “t”: 0, “b”: 0},  
> autosize=False,  
> scene=dict(  
> camera=dict(  
> # eye=dict(x=0.1, y=0.1, z=1.5)  
> ),  
> aspectratio=dict(x=1, y=.2, z=0.2),  
> aspectmode=“manual”,  
> ),  
> )
> 
> # push data into a data frame to enable more types of analysis
> 
> df = pd.DataFrame(  
> [  
> {  
> “bin\_name”: b.partno,  
> “bin\_index”: i,  
> \*\*packer\_to\_plotly(item),  
> “item\_typeof”: item.typeof,  
> \*\*{d: v for v, d in zip(item.getDimension(), list(“hwl”))},  
> \*\*{d + d: v for v, d in zip(item.position, list(“xyz”))},  
> }  
> for i, b in enumerate(packer.bins)  
> for item in b.items  
> ]  
> )
> 
> # print(“dataframe: \n”, df[‘item\_typeof’])
> 
> # create a figure for each container (bin)
> 
> for pbin, d in df.groupby(“bin\_name”):  
> fig = go.Figure()  
> xx =   
> yy =   
> zz =
> 
> # create a trace for each box (bin)
> 
> for _, r in d.iterrows():  
> # print("_, ", \_,)  
> # print("r ", r)  
> if r[“item\_typeof”] == ‘cube’:  
> fig.add\_trace(  
> go.Mesh3d(r[[“x”, “y”, “z”, “i”, “j”, “k”, “name”, “color”]].to\_dict())  
> )  
> xx += [r.xx, r.xx + r.h, r.xx + r.h, r.xx, r.xx, None] \* 2 + [r.xx] \* 5 + [None]  
> yy += [r.yy, r.yy, r.yy + r.w, r.yy + r.w, r.yy, None] \* 2 + [  
> r.yy,  
> r.yy + r.w,  
> r.yy + r.w,  
> r.yy,  
> r.yy,  
> None,  
> ]  
> zz += (  
> [r.zz] \* 5  
> + [None]  
> + [r.zz + r.l] \* 5  
> + [None]  
> + [r.zz, r.zz, r.zz + r.l, r.zz + r.l, r.zz, None]  
> )
> 
> ```
> fig.add_trace(
> go.Scatter3d(
> x=xx,
> y=yy,
> z=zz,
> mode="lines",
> line_color="black",
> line_width=2,
> hoverinfo="skip",
> )
> )
> else:
> name = r["name"]
> color = r["color"]
> radius = float(r["w"])/2
> height = float(r["l"])
> x_list = r["x"]
> # print("x_list: ", x_list)
> y_list = r["y"]
> # print("y_list: ", y_list)
> z_list = r["z"]
> x_min = float(min(x_list))
> # print("x_min ", x_min)
> x_max = float(max(x_list))
> # print("x_max ", x_max)
> y_min = float(min(y_list))
> y_max = float(max(y_list))
> x_cor = x_min + (x_max - x_min)/2
> y_cor = y_min + (y_max - y_min)/2
> z_cor = float(min(z_list))
> # print("xyz! ", x_cor,y_cor,z_cor)
> # colorscale = [[0, '#636EFA'], [1, '#636EFA']]
> # print("colorscale ", colorscale)
> fig.add_traces(
> # cylinder_traces(radius, x_cor, y_cor, z_cor, height, n_sub=1, line_kw={"line_color": "#202020", "line_width": 3})
> # )
> cylinder_traces(radius, x_cor, y_cor, z_cor, height, color, name, n_sub=1,
> line_kw={"line_color": "#202020", "line_width": 3}))
> 
> ```
> 
> x, y, z = parallelipipedic\_frame(0, 1202.4, 0, 235, 0, 269.7)
> 
> fig.add\_trace(  
> go.Scatter3d(  
> x=x,  
> y=y,  
> z=z,  
> mode=“lines”,  
> line\_color=“blue”,  
> line\_width=2,  
> hoverinfo=“skip”,  
> )  
> )
> 
> # -----------------newly added code to test plotting cylinder
> 
> # fig.add\_traces(
> 
> # cylinder\_traces(50, 0, 0, 0, 80, n\_sub=1, line\_kw={“line\_color”: “#202020”, “line\_width”: 3})
> 
> # )
> 
> # -----------------end for newly added code to test plotting cylinder-------------------
> 
> ar = 4  
> xr = max(d[“x”].max()) - min(d[“x”].min())
> 
> # fig.update\_layout(
> 
> # showlegend=False,
> 
> # title={“text”: pbin, “y”: 0.9, “x”: 0.5, “xanchor”: “center”, “yanchor”: “top”},
> 
> # margin={“l”: 0, “r”: 0, “t”: 0, “b”: 0},
> 
> # # autosize=False,
> 
> # scene=dict(
> 
> # camera=dict(eye=dict(x=2, y=2, z=2)),
> 
> # aspectmode=“data”,
> 
> # ),
> 
> # )
> 
> fig.update\_layout(  
> showlegend=False,  
> title={“text”: pbin, “y”: 0.9, “x”: 0.5, “xanchor”: “center”, “yanchor”: “top”},  
> margin={“l”: 0, “r”: 0, “t”: 0, “b”: 0},  
> # autosize=False,  
> scene=dict(  
> camera=dict(eye=dict(x=2, y=2, z=2)),  
> aspectratio={  
> \*\*{“x”: ar},  
> \*\*{  
> c: ((max(d[c].max()) - min(d[c].min())) / xr) \* ar  
> for c in list(“yz”)  
> },  
> },  
> aspectmode=“manual”,  
> ),  
> )
> 
> plotly.offline.plot(fig, filename=‘C:/Users/mike/Desktop/3D\_BinPack\_’ + str(row) + ‘.html’, auto\_open=False,  
> config={‘displaylogo’: False})
> 
> # fig.write\_html(‘C:/Users/mike/Desktop/3D\_BinPack\_’ + str(row) + ‘.html’, auto\_open=False,
> 
> # include\_plotlyjs=“cdn”,config={‘displaylogo’: False})
> 
> fig.show(config={‘displaylogo’: False})

---

<div class="post-metadata">

**Author:** ![jerry2021](https://avatars.discourse-cdn.com/v4/letter/j/ecc23a/32.png) [@jerry2021](https://community.plotly.com/u/jerry2021)\
**Post date:** [June 3, 2022, 3:55pm UTC](https://community.plotly.com/t/plotly-huge-html-file-size/64342/2 "2022-06-03T15:55:37Z")

</div>

any help regarding this issue?

---

<div class="post-metadata">

**Author:** ![naten7k](https://avatars.discourse-cdn.com/v4/letter/n/c2a13f/32.png) [@naten7k](https://community.plotly.com/u/naten7k)\
**Post date:** [June 3, 2022, 8:53pm UTC](https://community.plotly.com/t/plotly-huge-html-file-size/64342/3 "2022-06-03T20:53:57Z")

</div>

Your code is formatted a bit funny, which makes it hard to read. Any chance you can make a minimal example?

My primary hack to reduce file size has been to round values before plotting, since the values are stored as plaintext in the html file. For example, if you are writing values as `0.3978238382` when `0.40` would suffice, that cuts the file size by a factor of ~3. Not sure if this is something you’re already doing/able to do here, but figured I’d mention it.

That said, I am not a plotly dev, so there may be much better approaches to compression that I haven’t yet found. (If so, would love to hear about them).

---

<div class="post-metadata">

**Author:** ![jerry2021](https://avatars.discourse-cdn.com/v4/letter/j/ecc23a/32.png) [@jerry2021](https://community.plotly.com/u/jerry2021)\
**Post date:** [June 4, 2022, 9:51am UTC](https://community.plotly.com/t/plotly-huge-html-file-size/64342/4 "2022-06-04T09:51:58Z")

</div>

You don’t have to look at the mode code (I just copy all in case anyone want to actually run it to test). You just look at the plotly code par, the above plotly drawing codes will result in a very large html file when the quantity of items is big, e.g. 600+ items to be plotted in a container, which will result in 80MB file size. how can we avoid generating such a big size html file? I guess this is a result of “add\_trace” to individual item plot.
