DEV Community

Cover image for ~17 Billion Triangles in a Browser: How I Built CellVerse Ultra - a Living Interactive 3D Cell - in One Week
Devastor
Devastor

Posted on

~17 Billion Triangles in a Browser: How I Built CellVerse Ultra - a Living Interactive 3D Cell - in One Week

I wanted to see a cell not as a neat textbook diagram and not as a static collection of organelles, but as a dense, continuously moving world: a world with a membrane, proteins, a nucleus, endoplasmic reticulum, Golgi apparatus, mitochondria, ribosomes, RNA, cytoskeleton, vesicles, ions, pumps, and motor proteins.

And I wanted to see all of it directly in a browser - without Unreal Engine, without Unity, without a separate native client, and without a prebuilt Blender scene.

That is how CellVerse Ultra 7.5 appeared: a procedural browser engine for cellular visualization and dynamics, built on Three.js.

Project links:
https://cellverse.devastor.com/
https://cellverse.devastor.ru/
https://awesome.devastor.com/
https://awesome.devastor.ru/

Main stress-test record of the current version:

16,792,525,842 triangles in a single frame of a browser scene.

The built-in record table also stored the following values for that run:

  • 31,300 scene objects;
  • 27,427,916 particles and instances;
  • 100% detail;
  • beautiful mode;
  • 16,792,525,842 triangles.

I have not found another published interactive cell-visualization project - or, more broadly, another comparable browser visualization project - reporting a triangle count of this scale per frame. I therefore record this result as a CellVerse Ultra world record in the category of interactive browser-based 3D cell models.

This is the upper stress-test limit, not the normal operating mode. Ordinary work happens at much lower values, where the scene can be rotated, explored, rebuilt, filtered by layers, and observed in real time.

What CellVerse Ultra Is

CellVerse Ultra is not a single cell model and not a prerecorded animation. It is an engine that constructs a cellular scene at runtime from a set of interconnected generators.

Each major system exists as a separate module:

  • plasma membrane and membrane proteins;
  • nucleus, nuclear envelope, pores, nucleolus, and chromatin;
  • rough and smooth endoplasmic reticulum;
  • Golgi apparatus;
  • mitochondria and cristae;
  • lysosomes and peroxisomes;
  • microtubules and actin filaments;
  • DNA, RNA, mRNA, and tRNA;
  • free, nuclear, and ER-bound ribosomes;
  • ATP and ions;
  • vesicles;
  • kinesin and dynein;
  • membrane pumps and channels;
  • transparency, Fresnel effects, lighting, and post-processing;
  • the viscous cytosol medium effect;
  • presets, layers, statistics, profiling, and records.

The scene is not loaded from one heavy file. It is assembled programmatically, so it can change density, detail, quantitative ratios, material mode, and cell-type configuration.

By its nature, CellVerse lies between three fields:

  • scientific and educational cell visualization;
  • a real-time graphics engine;
  • a procedural simulation of qualitative cellular dynamics.

The project is not attempting atomistic molecular dynamics. It operates at a different scale: it aims to show the whole cell, its spatial architecture, relative proportions, transport, motion, and visual depth at the same time. The combination of all these goals inside one interactive browser scene is what makes the project unusual.

Why a Browser

A browser is usually treated as an environment for interfaces, websites, and relatively lightweight WebGL demos. I wanted to test the opposite idea: can an ordinary Chrome tab become a full cellular-visualization laboratory?

A browser architecture has major advantages:

  • launch by URL with no heavy client installation;
  • one unified HTML/CSS interface;
  • rapid experiments with parameters;
  • simple access to server-side statistics and a leaderboard;
  • easy embedding into an educational page;
  • instant switching between visual and technical representations;
  • portability across modern WebGL 2 systems.

The limitations of the browser do not disappear: one JavaScript main thread, garbage collection, WebGL limits, the cost of transparency, the cost of many draw calls, and the risk of context loss. Because of this, CellVerse had to be designed not as an ordinary scene, but as a system in which almost every subsystem is aware of object count, geometry reuse, and per-frame update cost.

Technology Stack

The current version uses:

  • Three.js 0.160.0;
  • WebGL 2;
  • JavaScript ES Modules;
  • procedural BufferGeometry;
  • InstancedMesh for large sets of repeated objects;
  • custom GLSL injections and shader materials;
  • EffectComposer with a switchable post-processing pipeline;
  • FastAPI and Uvicorn on the server side;
  • SQLite for users, sessions, and records;
  • standard HTML/CSS control panels.

The high-level architecture looks like this:

FastAPI / SQLite
        |
        v
HTML + CSS + ES Modules
        |
        v
CellVerse core
  |     |      |       |
  |     |      |       +--> UI, presets, leaderboard
  |     |      +----------> animation systems
  |     +-----------------> transparency and post-processing
  +-----------------------> procedural cell structures
                                |
                                v
                         Three.js / WebGL 2
Enter fullscreen mode Exit fullscreen mode

The server does not render the 3D scene. The entire scene lives on the client. The backend launches the application, supplies parameters, manages authentication, and stores results.

How the Cell Is Built

The scene is assembled in a defined order because structures depend on one another.

A simplified build sequence looks like this:

await createMembrane();
await createNucleus();
await createChromatin();
await createMitochondria(counts.mitochondria);
await createER();
await createGolgi();
await createLysosomes(counts.lysosomes);
await createPeroxisomes(counts.peroxisomes);
await createCentrioles();
await createMicrotubules(counts.microtubules);
await createActinFilaments(counts.actin);
await createDNA();
await createRNA();
await createRibosomes();
await createATP(counts.atp);
await createIons(counts.na, counts.k);
await createVesicles(counts.vesicles);
await createKinesin(counts.kinesin);
await createDynein(counts.dynein);
await createPumps(counts.pumps);
await createChannels(counts.channels);
Enter fullscreen mode Exit fullscreen mode

After geometry creation, the engine performs system-wide passes:

updateVisibility();
rebuildPickables();
rebuildTriangleIndex();
setupTransparency();
updateMembraneOpacity();
addMicroMovements();
updateStats(true);
Enter fullscreen mode Exit fullscreen mode

This lifecycle allows the complete cell to be recreated after changes to object counts, detail, presets, or display mode.

The scene remains manageable even at very high loads because rebuilding is centralized. Individual generators do not attempt to own the entire application. They receive context and return prepared groups, meshes, and state arrays.

A Unified Biological Scale

CellVerse is based on a centralized configuration of sizes, counts, speeds, and physical parameters.

The cell radius is the base unit:

CELL_RADIUS = 1.0;
Enter fullscreen mode Exit fullscreen mode

All other sizes are expressed relative to it:

NUCLEUS_RADIUS = 0.35;
MEMBRANE_THICKNESS = 0.005;
MITOCHONDRIA_RADIUS = 0.05;
MITOCHONDRIA_LENGTH = 0.4;
MICROTUBULE_OUTER_RADIUS = 0.0025;
ACTIN_RADIUS = 0.0012;
ATP_RADIUS = 0.00075;
ION_RADIUS_NA = 0.00032;
ION_RADIUS_K = 0.00037;
Enter fullscreen mode Exit fullscreen mode

This creates one coherent parameter space. Changing the scale of the cell does not require manually repositioning every structure type.

The configuration also contains biological reference counts:

Structure Reference count
Ribosomes 13,000,000
Mitochondria 1,700
ATP 5,000,000,000
Na+ ions 1,500,000,000
K+ ions 4,500,000,000
Membrane proteins 50,000
Microtubules 1,000
Actin filaments 10,000
Vesicles 20,000
Kinesin 50,000
Dynein 20,000
Na+/K+ pumps 800,000
Ion channels 100,000
Nuclear pores 3,000

The engine uses these values as a system of relationships and converts them into a representable number of visual instances.

The physical configuration includes:

  • temperature: 310 K;
  • cytoplasmic viscosity: 0.003;
  • cytoplasmic pH: 7.2;
  • lysosomal pH: 4.8;
  • membrane potential: -70 mV;
  • ATP concentration: 3 mM;
  • membrane fluidity and cytoplasmic-flow parameters.

These are not merely labels in the interface. Temperature, viscosity, speed, and scale participate in particle and organelle motion.

Two Object-Count Modes

CellVerse has two fundamentally different ways to set scene density.

Scalable Mode

The user works with practical visual counts: thousands of ribosomes, hundreds of mitochondria, thousands of ions, and so on. These values can be changed directly.

This is the primary mode for exploration and scene tuning.

Realistic-Count Mode

This mode uses biological reference values and one global extreme multiplier. It allows a smooth transition from a practical working scene to a stress test with tens of millions of instances.

Structures that must remain visible even at a very small multiplier use a representative floor. A minimum representative count preserves visual density, while an upper cap prevents a single structure type from destroying interactivity immediately.

The simplified idea is:

function representativeCount(realCount, multiplier, visualBase, cap) {
    const fromBiology = Math.max(1, Math.floor(realCount * multiplier));
    const fromVisualDensity = Math.max(1, Math.floor(visualBase * visualScale));
    return Math.min(cap, Math.max(fromBiology, fromVisualDensity));
}
Enter fullscreen mode Exit fullscreen mode

The separate Detail slider affects not only the number of instances, but also the segmentation of procedural geometry.

Three Display Modes

CellVerse includes three complete graphics profiles. These are not color themes: each mode has its own geometry, materials, and post-processing strategy.

Beautiful Mode

Beautiful mode is designed for maximum visual expression:

  • saturated but distinguishable colors;
  • complex organic forms;
  • transparent membranes;
  • Fresnel edge lighting;
  • shadows;
  • bloom;
  • depth of field;
  • surface microdetail;
  • high geometry segmentation;
  • a dense molecular environment.

This is the mode in which the cell looks like a vast self-contained world rather than a textbook diagram.

Realistic Mode

Realistic mode makes materials resemble wet, soft, lightly colored biological structures:

  • reduced saturation;
  • soft translucent shells;
  • lower emissive intensity;
  • calmer contrast;
  • a stronger sense of depth and fluidity;
  • separate index-of-refraction and roughness profiles.

Colors still distinguish structures, but the cell does not become a neon scene.

Engineering Mode

Engineering mode prioritizes readability and performance:

  • simpler geometry;
  • minimal expensive transparency;
  • disabled heavy post-processing;
  • clear forms;
  • high frame rate;
  • convenient analysis of object placement.

It is particularly useful for tuning counts, checking trajectories, and debugging scene architecture.

Procedural Geometry

One of the main features of CellVerse is that it does not rely on a library of ready-made organelle models.

Most objects are generated procedurally. Base spheres, capsules, tubes, and surfaces are deformed by several layers of noise, waves, local bulges, and asymmetry.

A basic fBm block looks roughly like this:

function fbm3D(x, y, z, octaves, persistence, lacunarity, seed) {
    let total = 0;
    let amplitude = 1;
    let frequency = 1;
    let normalizer = 0;

    for (let i = 0; i < octaves; i++) {
        total += noise3D(
            x * frequency,
            y * frequency,
            z * frequency,
            seed + i * 100
        ) * amplitude;

        normalizer += amplitude;
        amplitude *= persistence;
        frequency *= lacunarity;
    }

    return total / normalizer;
}
Enter fullscreen mode Exit fullscreen mode

A vertex of the base shape is then displaced along its normal:

const macro = fbm3D(nx * 8, ny * 8, nz * 8, 5, 0.6, 2.2, seed);
const micro = fbm3D(nx * 20, ny * 20, nz * 20, 3, 0.5, 2.0, seed + 100);
const displacement = macro * macroScale + micro * microScale;

position.addScaledVector(normal, displacement);
Enter fullscreen mode Exit fullscreen mode

This creates families of related organic forms rather than sets of identical perfect spheres.

Different structure types use different deformation profiles:

  • ribosomes receive large and small subunits, cavities, and protein-like roughness;
  • mitochondria are deformed capsules with an inner membrane and cristae;
  • the nucleus receives large-scale asymmetry;
  • membrane inclusions are generated as crystalline, droplet-like, and amorphous structures;
  • lysosomes and peroxisomes receive independent surface and size distributions.

Seeds preserve reproducibility while still producing variety.

Plasma Membrane

The membrane is one of the most complex systems in the project.

It includes:

  • outer and inner surfaces;
  • organic deformation;
  • separate membrane-protein classes;
  • several protein geometry prototypes;
  • inclusion clusters;
  • crystalline, droplet-like, and amorphous forms;
  • fluidity and slow drift;
  • user-controlled opacity;
  • distinct behavior in all three graphics modes.

Proteins are distributed across the spherical surface stochastically rather than on a uniform grid, with variations in scale, orientation, shape, and clustering.

Each instance receives a transformation matrix:

matrix.compose(
    surfacePosition,
    quaternionAlignedToNormal,
    instanceScale
);

instancedMesh.setMatrixAt(index, matrix);
Enter fullscreen mode Exit fullscreen mode

Instead of tens of thousands of separate Mesh objects, CellVerse uses several InstancedMesh objects sharing geometry and material. This sharply reduces draw calls and repeated GPU buffers.

Transparent Membrane Proteins

Membrane proteins required one of the most interesting solutions in version 7.5.

The classic transparency problem is well known: WebGL normally sorts transparent objects, but it does not sort individual instances inside one InstancedMesh. When front-side and back-side membrane proteins occupy the same instanced mesh, one renderOrder cannot place them correctly relative to the nucleus, Golgi apparatus, lysosomes, and other transparent organelles.

If proteins write to the depth buffer, front-side proteins begin to block transparent internal structures. If depth writing is disabled entirely, proteins on the far side of the membrane can visually rise above the nucleus.

CellVerse solves this without changing protein opacity, material, or geometry. Only the rendering pass is split.

The same instance set is drawn twice:

  • the far half of the membrane is rendered before transparent internal structures;
  • the near half is rendered after them.

The split is performed with a plane oriented relative to the camera direction. Pseudocode:

const backPass = originalInstancedMesh;
const frontPass = cloneUsingSameGeometryAndMaterial();

backPass.renderOrder = MEMBRANE_BACK;
frontPass.renderOrder = MEMBRANE_FRONT;

backPass.onBeforeRender = () => {
    sharedMaterial.clippingPlanes = [cameraFacingBackPlane];
};

frontPass.onBeforeRender = () => {
    sharedMaterial.clippingPlanes = [cameraFacingFrontPlane];
};
Enter fullscreen mode Exit fullscreen mode

The result:

  • far-side proteins correctly disappear behind the nucleus and organelles;
  • internal structures remain visible through near-side transparent proteins;
  • opacity, Fresnel response, shape, and instance count remain unchanged;
  • engineering mode does not receive an unnecessary second pass;
  • statistics do not double the object count because the technical front copy is marked separately.

The same method is applied not only to ordinary proteins, but also to crystalline, droplet-like, and amorphous membrane inclusions.

The Nucleus and Nuclear System

The nucleus is built as a complete subsystem:

  • outer and inner nuclear envelopes;
  • organic surface deformation;
  • nuclear pores;
  • nucleolus;
  • chromatin;
  • DNA;
  • nuclear ribosomes;
  • anchor points for the endoplasmic reticulum.

The nuclear shape is not restricted to a perfect sphere. Several spatial harmonics and deformation fields create large lobes and local irregularities. This becomes especially visible under side lighting and Fresnel edge illumination.

Chromatin is represented by distributed fibers, while selected DNA regions can be displayed as a double helix. The system is designed to show large-scale nuclear architecture and small molecular elements at the same time.

Endoplasmic Reticulum

The ER is divided into two visually and geometrically distinct parts.

Rough ER

The RER is constructed around the nucleus as a system of membrane sheets, volumetric lobes, and connecting bridges. Ribosomes are placed on its surface.

In version 7.5, the proportion of ER-bound ribosomes was increased so that the rough structure reads as a genuinely dense protein-synthesis zone rather than a smooth membrane with occasional dots.

Smooth ER

The SER is formed as a branching tube network. Curves, connecting segments, and geometry merging create an extensive network without thousands of separate draw calls.

The surface has a slow "breathing" motion: small coherent deformations that remove the feeling of a frozen model without turning the ER into rubber.

Mitochondria and Cristae

Each mitochondrion contains:

  • a deformed outer membrane;
  • an inner membrane;
  • matrix;
  • a set of cristae;
  • individual dimensions, rotation, and curvature;
  • soft motion and drift.

Cristae are actual geometry rather than a texture-only imitation. Their wavy surfaces add volume and remain readable in translucent modes.

A base mitochondrion is generated once as a prototype. Variations reuse its geometry and materials. Depending on display mode and count, the engine selects a more detailed or a cheaper representation.

Golgi Apparatus and Vesicular Transport

The Golgi apparatus is built as a stack of curved cisternae. Each cisterna has its own thickness, curvature, and spatial offset.

Regions around the Golgi act as sources and destinations for transport vesicles. Vesicles can move:

  • from the ER to the Golgi;
  • between Golgi regions;
  • from the Golgi to the plasma membrane;
  • toward internal compartments.

A trajectory combines directed transport, Brownian motion, and a soft background flow. When a vesicle reaches its destination, it receives a new source/target pair and continues the cycle.

A simplified update looks like this:

velocity.addScaledVector(directionToTarget, transportStrength * dt);
velocity.addScaledVector(brownianVector, diffusion * dt);
velocity.add(cytoplasmicFlow(position, time));

position.addScaledVector(velocity, dt);

if (position.distanceTo(target) < arrivalRadius) {
    chooseNextRoute();
}
Enter fullscreen mode Exit fullscreen mode

The nuclear and cell boundaries are respected, so particles should not freely fly through large closed structures or permanently escape beyond the membrane.

Lysosomes and Peroxisomes

Lysosomes and peroxisomes are separate dynamic systems, not differently colored variants of one sphere.

They differ in:

  • size ranges;
  • materials;
  • surface deformation;
  • color and transparency;
  • volumetric distribution inside the cell;
  • drift and pulsation parameters;
  • collision behavior with the nucleus and membrane.

For mass representation, the engine uses instances and state arrays. Visible geometry remains compact while transforms are updated through matrices.

Cytoskeleton

The cytoskeleton forms the spatial framework of the scene and simultaneously serves as a transport network.

Microtubules

Microtubules are curves extending from the centrosomal region toward the cell periphery. In beautiful mode, they use tubular geometry and local curvature.

Each curve is stored in userData, allowing motor proteins to retrieve a point and tangent anywhere along the route:

const position = curve.getPointAt(t);
const tangent = curve.getTangentAt(t);
Enter fullscreen mode Exit fullscreen mode

Actin

Actin filaments form a denser peripheral network and internal bundles. Their length, direction, and distribution depend on the region of the cell.

In engineering mode, both systems can switch to cheaper geometry while retaining their spatial relationships and transport paths.

Kinesin and Dynein


kinesin

Motor proteins in CellVerse genuinely move along microtubules.

For each motor, the system stores:

  • a reference to a microtubule curve;
  • the position parameter t;
  • direction;
  • speed;
  • stride phase;
  • step size;
  • cargo object;
  • states of two heads and two legs.

The primary movement along the route is compact:

motor.t += delta * motor.speed * globalSpeed * motor.direction;

if (motor.t > 1) motor.t -= 1;
if (motor.t < 0) motor.t += 1;

const position = motor.curve.getPointAt(motor.t);
const tangent = motor.curve.getTangentAt(motor.t);

motor.object.position.copy(position);
alignToTangent(motor.object, tangent);
Enter fullscreen mode Exit fullscreen mode

Visually, however, the motor does not slide as a rigid object. The heads move out of phase, the legs bend, and the cargo sways slightly:

const phase = motor.stridePhase;
const head1 = Math.sin(phase) * motor.stepSize * 0.5;
const head2 = Math.sin(phase + Math.PI) * motor.stepSize * 0.5;

leftHead.position.z = leftBaseZ + head1;
rightHead.position.z = rightBaseZ + head2;
leftLeg.rotation.z = leftBaseAngle + Math.sin(phase) * 0.4;
rightLeg.rotation.z = rightBaseAngle + Math.sin(phase + Math.PI) * 0.4;
Enter fullscreen mode Exit fullscreen mode

Kinesin and dynein use different sizes, speeds, and stride patterns. Kinesin has a base speed of approximately 0.0012, dynein approximately 0.0010, while direction is configured separately.

Ribosomes, RNA, and Translation Complexes

A ribosome is built from a large and a small subunit. The surfaces of both parts receive independent deformation profiles, so the result resembles a macromolecular complex rather than two perfectly smooth spheres.

The system distinguishes:

  • free ribosomes;
  • ribosomes on mRNA;
  • rough-ER ribosomes;
  • nuclear ribosomes.

mRNA is generated as a procedural curve along which several ribosomes can be placed, forming a polysome.

tRNA has its own curved geometry and is distributed throughout the cytosol.

At high counts, the engine uses pools of instanced subunits. This preserves the recognizable two-part shape of a ribosome while allowing thousands or millions of them to be displayed.

ATP, Ions, Pumps, and Channels

pump in the built-in 3D exporter

ATP

ATP is represented as a mass particle system with diffusive movement. Part of its logic is connected to transport and motor proteins.

Ions

Na+ and K+ are stored in typed arrays:

const positions = new Float32Array(count * 3);
const velocities = new Float32Array(count * 3);
const states = new Uint8Array(count);
Enter fullscreen mode Exit fullscreen mode

Every update accounts for:

  • Brownian motion;
  • temperature;
  • viscous damping;
  • reflection from the membrane;
  • whether the ion is inside or outside the cell;
  • interaction with channels;
  • transport by pumps.

The temperature multiplier is scaled by the square root of the temperature ratio, while viscosity affects damping.

Pumps

Na+/K+ pumps are placed on the membrane and have an animated cyclic state. Large-scale updates use instanced matrices and shader parameters.

Channels

Ion channels have an open/closed state. When an ion passes through an active channel, its membrane side changes.

Pump and channel prototypes are merged into compact geometry and then replicated through InstancedMesh.

Brownian Motion and Cytoplasmic Flow

To keep the scene from looking like a collection of objects on simple orbits, movement is divided into several components:

  • local random diffusion;
  • directed transport;
  • a slow general cytoplasmic flow;
  • pulsation of selected organelles;
  • surface micromovement;
  • attachment to structures such as microtubules or the ER.

A simplified general model is:

const thermal = Math.sqrt(temperature / referenceTemperature);
const damping = Math.exp(-viscosity * dt * dampingScale);

velocity.multiplyScalar(damping);
velocity.addScaledVector(randomDirection(), diffusion * thermal * dt);
velocity.add(cytoplasmicFlow(position, time));
position.addScaledVector(velocity, dt);
Enter fullscreen mode Exit fullscreen mode

As a result, particles do not move identically and do not synchronize into an artificial "swarm."

Cytosol Medium Effect

cytosol on the left, ordinary view on the right

A dedicated post-process creates the feeling that the camera is inside a dense intracellular medium rather than empty space.

The effect includes:

  • depth-aware scattering;
  • soft loss of contrast with distance;
  • colored fog;
  • refractive shimmer;
  • subtle chromatic aberration;
  • depth-based blur mixing;
  • vignette;
  • a separate profile for the nuclear region and karyoplasm.

To avoid a large, expensive blur kernel at full resolution, the effect uses a mip chain:

full resolution
      |
      v
    1/2 RT
      |
      v
    1/4 RT
      |
      v
    1/8 RT
Enter fullscreen mode Exit fullscreen mode

The final shader selects a blur level based on pixel depth.

Color can come directly from the scene or from the EffectComposer output. Depth is captured in a separate pass with MeshDepthMaterial so the effect can account for transparent systems that do not write ordinary depth.

After the membrane front/back pass was introduced, an interesting conflict appeared: a membrane-mesh callback could transfer a clipping plane to the shared depth material. The next frame's depth map would then become empty, turning the medium almost completely black.

The fix is isolated inside the depth pass:

  • the technical front copy is hidden temporarily;
  • the back-copy callback is suspended;
  • clipping planes on the shared depth material are reset;
  • depth is rendered once from the complete original geometry;
  • every state is restored through finally.

Pseudocode:

const saved = suspendMembraneSplitForDepth(scene);
const previousOverride = scene.overrideMaterial;

try {
    depthMaterial.clippingPlanes = null;
    scene.overrideMaterial = depthMaterial;
    renderer.render(scene, camera);
} finally {
    scene.overrideMaterial = previousOverride;
    restoreMembraneSplit(saved);
}
Enter fullscreen mode Exit fullscreen mode

This allows the color pass to preserve correct sorting of transparent proteins while the depth pass receives a complete membrane and a correct depth map.

Materials and Fresnel

Transparency in a cellular scene is more complex than opacity = 0.3.

Organelle profiles combine:

  • opacity;
  • roughness;
  • transmission;
  • thickness;
  • index of refraction;
  • depthWrite;
  • depthTest;
  • renderOrder;
  • Fresnel intensity;
  • edge and interior colors.

Fresnel makes thin translucent shells readable: the surface remains more transparent when viewed directly and becomes more visible at grazing angles.

A simplified fragment calculation:

float fresnel = pow(
    1.0 - max(dot(normalize(vNormal), normalize(vViewDir)), 0.0),
    fresnelPower
);

vec3 finalColor = mix(baseColor, edgeColor, fresnel * edgeStrength);
float finalAlpha = baseAlpha + fresnel * edgeAlpha;
Enter fullscreen mode Exit fullscreen mode

Every organelle type has its own profile. The nucleus, a mitochondrion, a vesicle, the Golgi apparatus, and a membrane protein do not share one universal transparency setup.

Post-Processing

The post-processing pipeline is built dynamically.

Depending on mode and toggles, it can include:

  • RenderPass;
  • ambient occlusion;
  • UnrealBloomPass;
  • depth of field through BokehPass;
  • FXAA or SMAA;
  • the custom cytosol effect.

Beautiful mode enables the fullest pipeline. Engineering mode bypasses expensive stages. Realistic mode uses softer parameters.

The key point is that post-processing is not hardwired. The user can disable effects and immediately observe their cost.

Why Millions of Objects Do Not Become Millions of Draw Calls

The foundation of CellVerse performance is InstancedMesh.

Three.js defines this class specifically for large numbers of objects that share geometry and material but have different transforms. Each instance has its own matrix, while the GPU reuses one vertex buffer.

A simplified example:

const mesh = new THREE.InstancedMesh(geometry, material, count);
const matrix = new THREE.Matrix4();

for (let i = 0; i < count; i++) {
    matrix.compose(position[i], rotation[i], scale[i]);
    mesh.setMatrixAt(i, matrix);
}

mesh.instanceMatrix.needsUpdate = true;
scene.add(mesh);
Enter fullscreen mode Exit fullscreen mode

CellVerse instances:

  • membrane proteins;
  • membrane inclusions;
  • ribosomes;
  • ATP;
  • ions;
  • pumps;
  • channels;
  • lysosomes and peroxisomes in mass modes;
  • part of the mitochondrial objects;
  • part of the transport objects.

Instancing does not "erase" the triangles of each instance. It reduces buffer duplication and draw calls. Vertex geometry is still processed for every visible instance. Therefore, when triangle count is measured, triangles in the base geometry are multiplied by instanceCount.

That is also how Three.js counts instanced draws in WebGLInfo: instanceCount * count / 3.

Geometry Merging

Where elements are different but static relative to one structure, CellVerse uses geometry merging.

For example, an ER network, a channel prototype, or a complex pump component may be generated from many primitives, but their geometries are merged before insertion into the scene.

This produces:

  • fewer objects in the scene graph;
  • fewer material switches;
  • fewer draw calls;
  • cheaper scene traversal;
  • more compact statistics.

Instancing and merging solve different problems and are used together.

Caches and Resource Reuse

Procedural generation can be expensive, so identical prototypes are not recreated for every object.

The engine caches:

  • geometries by parameter set;
  • materials;
  • shader variants;
  • protein prototypes;
  • ribosome shapes;
  • helper curves and templates;
  • temporary vectors and matrices used by hot loops.

A cache key is built from type, mode, segmentation, scale, and seed profile:

const key = `${type}:${mode}:${segments}:${scale}:${variant}`;

if (!geometryCache.has(key)) {
    geometryCache.set(key, createGeometry(params));
}

return geometryCache.get(key);
Enter fullscreen mode Exit fullscreen mode

During a complete rebuild, old GPU resources are released with dispose, and references are removed from groups and state arrays.

Typed Arrays and the Hot Loop

Mass particle systems are not stored as millions of JavaScript objects shaped like {x, y, z, vx, vy, vz}. They use compact typed arrays.

This reduces:

  • garbage-collector pressure;
  • per-object metadata;
  • indirect memory access;
  • memory fragmentation.

Shared Vector3, Quaternion, and Matrix4 objects are preallocated for temporary calculations:

const tmpPosition = new THREE.Vector3();
const tmpQuaternion = new THREE.Quaternion();
const tmpScale = new THREE.Vector3();
const tmpMatrix = new THREE.Matrix4();
Enter fullscreen mode Exit fullscreen mode

They are reused inside loops instead of being created again every frame.

Different Update Frequencies for Different Subsystems

Not everything needs to update 60 times per second.

In CellVerse:

  • the camera and primary render loop run every frame;
  • motors and transport update at high frequency;
  • micromovement of selected structures can update every second frame;
  • statistics are recalculated every few seconds;
  • hidden layers are skipped;
  • static instances use StaticDrawUsage;
  • dynamic matrices use DynamicDrawUsage.

Delta time is capped so that returning from a frozen or background tab does not make objects perform one enormous jump:

const delta = Math.min(clock.getDelta(), 0.05);
Enter fullscreen mode Exit fullscreen mode

Animation systems are isolated. An error in one movement group should not stop the entire cell loop.

WebGL Context Stability

CellVerse listens for WebGL context-loss and restoration events.

When context is lost, the application:

  • stops unsafe operations;
  • displays the state to the user;
  • preserves the logical cell configuration.

After restoration:

  • render targets are recreated;
  • materials and post-processing are restored;
  • the scene can be rebuilt without restarting the server.

Additional safeguards include:

  • development assets served without aggressive caching;
  • an error screen;
  • restart and simplified-mode paths;
  • release of previous resources during rebuilding;
  • exclusion of technical meshes from picking and statistics.

How the Triangle Count Is Calculated

The built-in counter walks visible Mesh objects and calculates triangles in each base geometry:

const trianglesPerGeometry = geometry.index
    ? Math.floor(geometry.index.count / 3)
    : Math.floor(geometry.attributes.position.count / 3);
Enter fullscreen mode Exit fullscreen mode

For an ordinary mesh, the value is added once. For InstancedMesh, it is multiplied by the number of instances:

triangles += trianglesPerGeometry * (
    object.isInstancedMesh ? object.count : 1
);
Enter fullscreen mode Exit fullscreen mode

The complete formula is:

triangles in the frame =
Σ (triangles in base geometry × number of visible instances)
Enter fullscreen mode Exit fullscreen mode

This is the standard meaningful way to count triangles in an instanced scene. The original Three.js WebGLInfo.js uses the same principle:

render.triangles += instanceCount * (count / 3);
Enter fullscreen mode Exit fullscreen mode

Therefore, 16,792,525,842 is the number of triangles in the visible scene with all instances included, not a sum of decorative configuration values.

Instancing makes storage and submission of repeated geometry much more efficient, but every instance remains a separate geometric presence in the frame. If a base protein contains 10,000 triangles and is drawn 100,000 times, those protein instances represent one billion triangles in the scene.

Technical front/back membrane copies are deliberately excluded from the counter so that the same protein set is not counted twice merely because of the transparency technique.

The 16,792,525,842-Triangle Record


almost 17 billion triangles

The record was saved by the application itself in SQLite rather than copied manually from a screenshot.

The record row contains:

mode       = beautiful
triangles  = 16,792,525,842
objects    = 31,300
particles  = 27,427,916
detail     = 100
fps        = 0
Enter fullscreen mode Exit fullscreen mode

The zero FPS value in the table is a rounded result in the extreme mode. A frame takes a very long time, but the application preserves the context and the constructed scene.

That is the key result for me: a browser engine running on standard Three.js and WebGL sustained a scene whose logical geometric complexity approached 17 billion triangles - without Nanite, without a native C++ renderer, and without a custom driver layer.

Record Hardware

The test system was:

  • THUNDEROBOT ZeroG4Radiant laptop;
  • Intel Core i9-14900HX;
  • 80 GB RAM;
  • NVIDIA GeForce RTX 4090 Laptop GPU;
  • 16 GB dedicated VRAM;
  • Windows 11 Pro 64-bit;
  • DirectX 12;
  • 3840 × 2400 internal display.

This is a powerful laptop, but it is still an ordinary consumer machine - not a server GPU cluster and not a specialized graphics workstation with hundreds of gigabytes of VRAM.

The Interface as a Laboratory Console

interface with the profiler enabled on the right

The UI is divided into several sections.

Main Parameters

  • detail;
  • extreme multiplier;
  • membrane transparency;
  • global scale;
  • time speed;
  • real-time updates;
  • cache clearing.

Quantitative Ratios

The counts of key structures can be changed independently or proportionally.

Layer Visibility

Every major system can be enabled or disabled independently. This allows the cell to be explored layer by layer:

  • hide the membrane and look inside;
  • leave only the cytoskeleton;
  • show only the nucleus and ER;
  • study transport;
  • disable particles and keep organelles.

Presets

The project includes ready-made configurations:

  • default biological profile;
  • cytoskeleton demonstration;
  • engineering profile;
  • membrane study;
  • nucleus focus;
  • realistic counts.

There are also cell-type profiles: hepatocyte, neuron, fibroblast, erythrocyte, myocyte, and plant cell. They change organelle ratios and visual configuration.

Viewing Tools

  • wireframe;
  • fullscreen mode;
  • profiler;
  • hints;
  • object selection;
  • structure information;
  • leaderboard;
  • pause and scene rebuild.

Object Selection and Information

Objects that participate in selection are collected in a separate pickables list.

The Raycaster does not need to test every technical mesh in the scene. Front/back technical copies, helper objects, and label elements are excluded.

After selection, the engine can identify the structure type, display its description, and highlight it visually.

For complex instanced groups, instanceId is preserved, making it possible to distinguish individual instances inside one InstancedMesh.

Server Side and Records

FastAPI hosts the application and its API. SQLite stores:

  • users;
  • sessions;
  • records by display mode;
  • triangle count;
  • FPS;
  • object count;
  • detail level;
  • particle count;
  • update time.

The best result is stored for each user and mode. This turns the stress test into a reproducible part of the application rather than an accidental one-off experiment.

Comparison with Existing Systems

CellVerse overlaps with several classes of software, but I have not found a direct analogue that combines the same set of capabilities.

Three.js

Three.js is the rendering foundation, not a ready-made cell engine. It provides the scene graph, materials, geometry, shaders, cameras, InstancedMesh, and post-processing. The complete biological architecture, generators, dynamics, modes, and CellVerse interface were built on top of it.

The result is not simply "running Three.js." It is a specialized engine built with Three.js that can maintain tens of millions of instances and billions of triangles in a cellular scene.

Unreal Engine 5 and Nanite

Nanite is one of the strongest modern approaches to extremely detailed geometry. It divides meshes into hierarchical clusters, selects detail according to screen coverage, streams only the required data, and uses a specialized path that avoids traditional draw-call limitations.

CellVerse solves a different problem in a different way:

  • it runs in a browser;
  • it uses WebGL and ordinary instanced geometry;
  • it has no Nanite-style virtualized cluster renderer;
  • it moves millions of biological instances simultaneously;
  • it constructs the scene procedurally at startup;
  • it keeps every logical structure type accessible through the UI.

By raw triangle count in an instanced scene, the 16.8-billion record is orders of magnitude beyond traditional real-time WebGL scenes and far above examples in the hundreds-of-millions range.

A direct FPS comparison between the CellVerse stress mode and Nanite would be meaningless. Nanite is explicitly designed to reject invisible and subpixel geometry and avoid processing the full source polygon count. The strength of CellVerse lies elsewhere: a standard browser graphics stack retains and manages an enormous procedural cellular scene without specialized geometry virtualization.

This is not an attempt to reproduce Unreal inside a browser tab. It is proof of how far a specialized browser engine can be pushed when the entire architecture is designed around one problem.

Mol* and Mesoscale Explorer

Mol* is a powerful browser toolkit for molecular visualization. It handles large biomolecular structures, trajectories, and models with tens of millions of atoms.

Mesoscale Explorer extends this direction toward integrative models of viruses, bacteria, and cellular organelles.

Their major strength is their connection to real molecular data and specialized scientific representations.

CellVerse emphasizes something different:

  • procedural generation of an entire eukaryotic cell;
  • organelles, molecules, and transport in one scene;
  • three complete artistic and technical display modes;
  • real-time modification of quantitative ratios;
  • custom dynamics for motor proteins, vesicles, ions, and membrane systems;
  • construction of cell states rather than only viewing loaded coordinates.

CellPAINT

CellPAINT allows users to compose molecular and cellular scenes from scientifically grounded ingredients. It is a strong biological illustration and composition tool.

CellVerse differs through a fully three-dimensional procedural scene, a free camera, large-scale animation, and stress scaling of object counts.

cellPACK and CellVIEW

cellPACK creates 3D models of cellular environments from recipes for membranes, membrane proteins, and soluble molecules. CellVIEW was built for fast rendering of very large molecular scenes in Unity.

These are among the closest projects in spirit regarding density and mesoscale biology.

CellVerse adds its own combination:

  • immediate browser launch;
  • procedural organelle generation;
  • dynamic count rebuilding;
  • three display modes;
  • live transport;
  • a unified laboratory interface;
  • a built-in record-oriented stress mode.

Simularium

Simularium is designed for publishing and analyzing trajectories of biological simulations directly in a browser.

CellVerse does not require a precomputed trajectory. It creates and updates its own cellular environment in real time.

Allen Cell Viewer

The Allen 3D Cell Viewer and other Allen Institute tools allow users to explore real 3D images of cellular structures and microscopy data.

CellVerse is a generative scene model: it creates a synthetic, controllable, dynamic cell that can be scaled far beyond one microscopy dataset.

Summary Comparison

System Browser Whole cell Procedural generation Real-time dynamics Adjustable counts Billion-triangle stress tests
CellVerse Ultra Yes Yes Yes Yes Yes Yes
Mol* / Mesoscale Explorer Yes Partial / models No, loaded data Trajectories and interaction Limited Not reported
CellPAINT Browser / standalone Illustrative Ingredient-based scene Partial Yes Not reported
cellPACK / CellVIEW Partial / desktop Yes Recipe-based packing Visualization Yes Large atomic scenes
Simularium Yes Depends on data No Simulation playback Depends on model Not reported
Allen Cell Viewer Yes Real cellular data No Interactive viewing No Not reported
Unreal Engine Nanite No, native engine General-purpose scene Depends on project Yes Depends on project Virtualized geometry

What Makes the Project Especially Strong

1. Coherence

Large organelles, membrane systems, cytoskeleton, molecules, particles, and transport all coexist in one scene.

2. Procedural Construction

The cell is not frozen into one file. It can be rebuilt, scaled, and configured.

3. Visual Depth

Transparency, Fresnel lighting, front/back membrane passes, and the depth-aware cytosol create a genuine sense of being inside a volume.

4. Scale

Instancing, merging, typed arrays, and caches allow the engine to move from a normal interactive scene to tens of millions of instances.

5. Motion

Kinesin and dynein walk along microtubules, vesicles move between compartments, ions diffuse and pass through membrane systems, organelles drift, and surfaces breathe.

6. Control

Every layer can be disabled, counts can be changed, display mode can be switched, the scene can be rebuilt, and the result can be stored as a record.

7. Browser Architecture

All of this operates in an ordinary browser tab without a native game engine.

Where CellVerse Can Go Next

The architecture already supports several clear directions.

WebGPU and Compute Shaders

WebGPU can move more particle dynamics from the CPU to the GPU and increase the interactive instance count.

GPU Culling and Spatial Clusters

The cell can be divided into spatial blocks so structures irrelevant to the current camera view do not need to be updated.

Order-Independent Transparency

The current front/back method works extremely well for a spherical membrane. The next level is weighted blended OIT or depth peeling for arbitrary intersections between transparent organelles.

Deeper Biological Dynamics

Future development can include:

  • detailed pump cycles;
  • concentration gradients;
  • microtubule polymerization;
  • protein synthesis and transport;
  • endocytosis and exocytosis;
  • mitochondrial ATP production;
  • cellular signaling cascades.

Multiple Cells

The next major scale is tissue: cell-cell contacts, extracellular matrix, and exchange of substances.

Conclusion

CellVerse Ultra began as an attempt to see a cell as a living, coherent world. In one week, it grew into a large specialized engine with its own procedural geometry, transparency system, dynamics, post-processing, interface, server, and record table.

It can already:

  • construct a complete cell directly in a browser;
  • display dozens of systems simultaneously;
  • change their counts and detail;
  • switch between beautiful, realistic, and engineering modes;
  • animate intracellular transport;
  • operate with millions of instances;
  • survive a stress scene containing 16,792,525,842 triangles.

For interactive browser-based cellular visualization, this is a world-class result - especially considering that the project was created by one person in an extremely short time and runs on a standard Three.js/WebGL stack.

The question is no longer whether a browser can display a living cellular universe. It can.

The question is how far that universe can be scaled.

Links

Contact

For technical questions, discussion, or collaboration:

Top comments (0)