Skip to content

Decimate published neuron meshes and serve them compressed #456

Description

@Robbie1977

Follow-up to #455, which contained the problem in the viewer and the transport but left the data itself unchanged.

The meshes are undecimated

volume_man.obj is published at full resolution. APL_L (VFB_jrmc3hpm) is 513,692,448 bytes — 5,677,124 vertices and 11,402,252 faces — for a single neuron. The hemibrain copy of the same cell is 7,537,079 bytes, so this is not inherent to the arbor.

Measured 2026-08-25, all aligned to JRC2018U unless stated:

Neuron volume_man.obj volume.obj
APL_L MaleCNS VFB_jrmc3hpm 513,692,448 absent
APL_R MaleCNS VFB_jrmc3hpl 539,778,360 absent
APL_R OpticLobe VFB_00103m9x 656,186,044 3,526,734
APL FlyWire VFB_fw036977 295,091,332 295,091,332 (same file)
APL_R hemibrain VFB_jrchjrhd on JRCFIB2018Fum absent 7,537,079
10 sampled MaleCNS Kenyon cells 3.7–6.3 MB absent

It scales with arbor size rather than with dataset, so it affects large neurons everywhere. Even with the guard from #455 a 5.68 M-vertex mesh is roughly 140 MB of GPU buffers before parse overhead, so decimation is what makes these neurons viewable at all rather than merely non-fatal.

Suggested target is the ~10 MB hemibrain already achieves.

MaleCNS has no volume.obj

Every MaleCNS individual publishes volume_man.obj and nothing smaller, so there is no decimated asset to prefer. Generating one, and preferring it over volume_man.obj in the KB obj field where it exists, would let the viewer use the full mesh for far more neurons than it can today.

The data host serves .obj uncompressed

gzip on is set without gzip_types, and nginx's default is text/html only. Every data file is served as application/octet-stream (the default_type), so meshes and skeletons go out uncompressed while the directory listing is compressed. Adding gzip_types, gzip_vary and gzip_static in Robbie1977/Docker-nginx-fileserver fixes it. Measured on a real 5,795,778 byte mesh: level 1 gives 2.69x in 87 ms, level 4 2.99x in 147 ms, level 6 3.15x in 448 ms, and a pre-built .gz 3.15x in 14 ms. On-the-fly compression cost grows with the file, so the pipeline emitting .obj.gz alongside the decimated meshes would be worth having.

Separately, responses carry three Cache-Control headers from that config plus a fourth private added by the front proxy. private last wins for shared caches, which quietly defeats the public, immutable the file server is setting.

Longer term: pass the URL, not the geometry

ThreeDEngine.loadThreeOBJModelFromNode calls loader.parse(node.obj), so the mesh text travels inside the resolved model. THREE.OBJLoader.load(url, onLoad, onProgress, onError) already exists unused at geppetto-client/js/components/interface/3dCanvas/OBJLoader.js:42, and the URL is already on the unresolved import type client-side. Loading directly in the browser would remove the server round-trip and the model copy entirely, and give real load progress and a year of browser caching. Colour, opacity and visibility would need to stay bound to the existing instance handle or the controls break.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions