Documentation
How BoxoLand turns a cube resolution into DNA sequences: the geometry model, the bond assignment algorithm, free energy filtering, plates and picklists.
Overview
BoxoLand designs DNA origami wireframe cube assemblies. You pick a resolution β how many cubes along X, Y and Z β and the app builds the corresponding lattice of wireframe cubes in 3D. Each cube is a DNA origami monomer whose 12 edges are DNA helix bundles; cubes stick to their neighbours through bonds: short single-stranded overhangs that extend from the edges of the two touching faces.
The pipeline is: resolution β lattice β interface detection β bond assignment β
sequence export β plate picklist. Everything below documents one of those stages.
Cube geometry & edge numbering
Every cube has 8 vertices and 12 edges numbered 1β12. The numbering is
fixed across the whole app β it is what makes bond labels (B01βB12),
sequence names, and interface tables line up.
| Group | Edges |
|---|---|
| Bottom face | 1 = bottom-front, 2 = bottom-right, 3 = bottom-back, 4 = bottom-left |
| Top face | 5 = top-front, 6 = top-right, 7 = top-back, 8 = top-left |
| Vertical | 9 = front-left, 10 = front-right, 11 = back-right, 12 = back-left |
Edge n carries the bond label B + n zero-padded to two digits, so edge 7 β B07.
The canonical definition β vertex coordinates, face membership and interface mappings β
lives in app/assets/config/edge_numbering.json. Toggle
Show Edge Labels in the sidebar to see the numbers rendered on the model.
Assembly generation
The X / Y / Z Cubes inputs drive a triple loop over lattice sites. Each
site gets a cube whose id is its coordinate string "x,y,z", and whose world
position is site index Γ spacing per axis. Spacing depends on the assembly style:
| Style | Spacing | Meaning |
|---|---|---|
| Face-to-Face | cubeSize Γ 1.3 |
Cubes meet face to face with room to draw the bonds between them. |
| Edge-to-Edge (hexamer) | cubeSize Γ β2 / 1.25 |
Diagonal block spacing. Only a centre cube (at even coordinates) and its 6 axis-adjacent neighbours are kept, giving 7-cube hexamer blocks. |
After the lattice is built the group is re-centred on the origin, and the reference ground grid is rebuilt to match: one grid cell per cube spacing, spanning the assembly footprint plus a one-cell margin, placed just under the lowest cube. Change the resolution and the grid grows with it.
Performance: above 64 cubes (and with per-edge colour coding off) the renderer switches to instanced meshes β one draw call per edge slot instead of per cube β which is what keeps large lattices interactive.
Interface detection & families
An interface is a pair of cubes that touch face to face. Detection walks
the cube list and, for each cube, checks the +x, +y and
+z neighbour; using only the positive directions counts each interface exactly
once. Interfaces are grouped into three families by the axis along which
the cubes touch, because the two faces involved expose a different set of 4 edges each:
| Family | Axis | Cube 1 edges | Cube 2 edges |
|---|---|---|---|
| Family 1 | FrontβBack (Y) | [1, 5, 9, 10] |
[3, 7, 11, 12] |
| Family 2 | LeftβRight (X) | [2, 6, 10, 11] |
[4, 8, 9, 12] |
| Family 3 | TopβBottom (Z) | [5, 6, 7, 8] |
[1, 2, 3, 4] |
Each interface therefore involves 4 bonds, and each cube edge can
participate in two different families β which is why assignments are keyed
edge_family (for example 10_2 = edge 10 acting in family 2).
Edge/family slots that no interface uses are marked X (inert).
Bond assignment algorithm
Every one of the 4 edges of an interface gets one of 8 bond variants
(V1βV8). Two cubes bind only if their facing edges carry the
same variant, so the assignment is what encodes which cube docks with which.
The goal is orthogonality: two different interfaces in the same family
should not look alike, or cubes will bind in the wrong place.
1. The overlap constraint
Overlap between two interfaces is the number of positions (out of 4) where they share the same variant. The slider sets the ceiling:
maxOverlapCount = floor(maxOverlapPercent / 100 Γ 4)
| Setting | Shared bonds allowed | Effect |
|---|---|---|
| 0% | 0 | Every interface fully unique β hardest to satisfy. |
| 25% default | 1 | Maximum practical diversity. |
| 50% | 2 | Looser; easier on large assemblies. |
| 75% | 3 | Weak orthogonality. |
| 100% | 4 | Interfaces may be identical. |
The constraint is checked pairwise within each family only β interfaces in different families use disjoint edge sets, so they cannot be confused with one another.
2. Randomised constraint satisfaction
Rather than solving the assignment exactly (the search space is 84 = 4096 combinations per interface), the app samples:
- Assign random variants to every interface in the assembly.
- Validate: no same-family pair may exceed
maxOverlapCount, and β if energy filtering is on β every interface must fall inside the energy window. - Reject and retry until a fully valid assignment is found, or the attempt budget runs out.
- Repeat to collect a pool of valid solutions (Number of Random Combinations controls how hard the search tries).
3. Picking the winner
From the pool, the solution with the lowest average pairwise overlap wins β satisfying the constraint is the floor, and this step maximises the margin above it.
4. Projecting onto cubes
The winning interface-level assignment is expanded into per-cube data: for each interface,
the 4 variants are written to cube 1's edges and to cube 2's matching edges, so
both partners carry the complementary pair. The result is
cubeAssignments["x,y,z"].edgeAssignments["edge_family"] = "V3", which is what
the 3D bond rendering, the sequence export and the plate picklist all read.
Bond assignments are saved with the assembly, so reopening a saved assembly restores them β including for Add to Plate and Export Sequences β without re-running the algorithm.
Hierarchical assembly
Hierarchical assembly lets you build a stage-2 lattice whose unit cell is itself a complete DNA origami cube assembly β the building block. This produces two-level structures: the building block encodes stage-1 bonds; the outer lattice encodes stage-2 bonds that must be orthogonal to the stage-1 ones so the two assembly levels do not interfere.
Selecting a building block
In the Building Block section of the sidebar, type the name of any saved assembly. Once selected the 3D viewport re-renders each super-cube position as a scaled-down replica of the building block, complete with its stage-1 bond overlays. Three rendering tiers keep large assemblies responsive:
- < 2,000 sub-cubes β full per-cube detail (individual meshes).
- 2,000 β 80,000 sub-cubes β global
InstancedMesh: one draw call for all cylinder edges and one for all torus rings, regardless of assembly size. - > 80,000 sub-cubes β wireframe LOD: one merged
LineSegmentsobject for the entire scene, with a notification explaining the simplified view.
Stage-1 bond assignment (inside the building block)
Run Assign Bonds on the building block assembly itself first (before selecting it as a building block for a larger design). The algorithm assigns 4 bond variants (V1βV8) to each of the 4 edges on every face-to-face interface, subject to the maximum overlap constraint. The result is saved with the assembly and loaded automatically when you select it as a building block.
Stage-2 bond assignment (between building blocks)
With a building block selected, clicking Assign Bonds on the outer assembly runs a second bond-assignment pass that treats each building-block position as a single unit. The solver assigns 4 bond variants per stage-2 interface (i.e. per pair of adjacent building blocks) while simultaneously enforcing:
- Intra-stage-2 overlap β controlled by the main Maximum Overlap % slider: how similar any two stage-2 interfaces in the same family can be.
- Inter-stage orthogonality β controlled by the per-sub-cube bond-count dropdowns: how many bond positions in a stage-2 interface are allowed to reuse a variant that already appears in stage-1.
Stage-1 variants are extracted automatically from the saved building-block assignment
via extractStage1Variants. The solver biases its random draws away from
those variants for the first 70% of attempts, then falls back to the full V1βV8 pool
if the constrained space is exhausted.
Per-sub-cube bond-count dropdowns
When a building block is selected the Stage 1 β Stage 2 Orthogonality panel shows one dropdown (0β4, default 4) for every sub-cube on each interface face:
- X-axis face β sub-cubes in the YZ plane of the building block, one per (y, z) position, repeated for each stage-2 X-axis interface.
- Y-axis face β sub-cubes in the XZ plane, repeated for each stage-2 Y-axis interface.
- Z-axis face β sub-cubes in the XY plane, repeated for each stage-2 Z-axis interface.
Setting a sub-cube to k means only the first k of the 4 bond pairs at that sub-cube position are drawn in the visualization (and counted in the overlap metric). Setting it to 0 hides all bonds for that sub-cube. The display card shows chosen / total (percentage) updating in real time.
Bond overlap analysis panel
After every successful bond assignment a compact panel appears in the bottom-right corner of the screen showing:
- Intra-stage average overlap % β the mean pairwise overlap across all interface pairs within each family, expressed as a percentage of 4 edge positions. Colour coded: green < 15%, yellow 15β35%, red > 35%.
- Per-family breakdown β X, Y, Z axes listed separately so you can see which axis is under-orthogonalised.
- Inter-stage overlap % (hierarchical mode only) β the fraction of stage-2 bond positions that share a variant with stage-1. Zero means perfect orthogonality between levels.
The panel closes with the Γ button and re-opens automatically on the next assignment run.
Free energy filtering
Orthogonality alone does not guarantee that interfaces are equally strong. Each
(family, bond, variant) triple has a pre-computed Gibbs free energy from
nearest-neighbour stacking energies of its DNA sequence, in kcal/mol.
- An interface's energy is the sum of its 4 bonds.
- With filtering enabled, a candidate interface is rejected unless its total sits inside your minβmax window.
- The modal shows the recommended range derived from all available sequences, and warns if your window is unreachable.
- After assignment you get the mean Β± standard deviation and the min/max across interfaces β a tight spread means uniform interface strength, which translates into cleaner folding and assembly yield.
Tightening the energy window shrinks the pool of legal variant combinations, so a narrow window plus a low overlap percentage is the usual reason a search fails.
Sequence export
Export Sequences writes a CSV containing every oligo needed to fold and assemble the design. Per cube that is roughly 169 sequences:
- ~145 core staples β the wireframe cube itself; identical for every cube.
- 24 bond sequences β 12 edges Γ 2, chosen from the assigned variant for each
edge_familyslot.
Bond names are resolved as F2F_F{family}_B{edge}_V{variant}, looked up in the
bond library to get the DNA sequence, then matched against the master sequence table to
recover plate/well/concentration metadata. Unassigned slots resolve to the inert
X sequence so the strand count per cube stays constant.
Plates & picklists
The Plate Manager tab models physical 96-well (8Γ12, rows AβH) and 384-well (16Γ24, rows AβP) plates. Add to Plate puts one cube per well, either into wells you type in or via auto-fill of the next free wells; with multi-plate auto-fill, assemblies larger than a plate spill into freshly created plates.
Each well records the concentrations used to compute transfers:
target staple conc (nM) = excess Γ scaffold conc transfer volume (Β΅L) = (target conc (nM) Γ final volume (Β΅L)) / (source conc (Β΅M) Γ 1000)
Export Picklist sends the plate to the server, which joins every sequence against the mother-plate specification and emits an acoustic-liquid-handler CSV with source plate/well, destination plate/well and transfer volume per row. Sequences missing from the mother plate fall back to your stated stock concentration and are flagged in the log.
Editing, nanoparticles & MEEP
- Edit Mode β click individual cubes to select them and attach cargo.
- Nanoparticles β place a particle inside a cube's cavity with a material, radius and colour; the tracker panel lists every cube and its particles, and supports batch add/remove across a selection.
- Deleting cubes β hover a cube and press Delete to carve it out of the lattice. Deleted sites are stored with the design and excluded from interface detection, so bonds must be re-assigned after a change.
- Export MEEP β emits a Python script describing cubes, nanoparticles, materials, source and field monitors for FDTD electromagnetic simulation in MEEP.
Import / Export round-trips the whole design as JSON β parameters, cube positions and neighbours, deleted sites, and bond assignments.
Accounts, saving & sharing
Sign-in is email + password. Without an account you can still explore: change the resolution, switch styles, edit or delete cubes, add nanoparticles, and browse public assemblies in the catalog. Account-only features β bond assignment, plates and picklists, sequence export and saving β prompt you to log in.
- Save stores the assembly in the database against your account, bond assignments included, and puts you on its
/assembly/:idURL. Saving again updates that record in place. - Catalog lists your assemblies plus everything published by others; filter by All / Public / My Saved / Local.
- Publish flips an assembly public so it shows up in the shared catalog; unlist to make it private again.
- Share mints a random token and returns a
/shared/<token>link that renders a read-only view of the design. - Local assemblies are legacy designs still held in this browser's storage; use Import on the card to copy one into your account.
Because bond assignments live inside the saved design data, reopening an assembly restores the bonds directly β which is what lets Add to Plate work on an existing assembly without re-running the assignment.
Interaction reference
| Input | Action |
|---|---|
| Left-drag | Orbit the camera |
| Right-drag | Pan |
| Scroll / middle-drag | Zoom |
| Arrow keys | Pan |
| Hover + Delete / Backspace | Delete the hovered cube |
| Click a bond | Show its family, edge, variant and sequence |
| Click a cube (Edit Mode) | Select it / open the nanoparticle dialog |
| Fullscreen button | Expand the 3D viewport |