igfold2d

Integrating IgFold2D into iCn3D

IgFold2D is a small, dependency-free JavaScript widget that renders a 2D proteomap of an Ig-fold domain — residues placed at canonical positions on a folded-lace grid indexed by the IgStrand universal numbering scheme (Tawfeeq et al., PLOS Comput Biol 2025).

This document is written for the iCn3D maintainer to evaluate integration. It covers what the widget needs as input, what existing iCn3D code already produces it, and three integration paths from lightest (no iCn3D change) to deepest (a new iCn3D panel).

What the widget needs

Two JSON inputs plus optional source metadata:

1. A template — defines the 2D grid

{
  "name": "IgV split A/A' (CD8-like)",
  "rows": 36,
  "cols": 14,
  "left_cells": [ ... ],      // grid cells: position, fill, borders, kind
  "right_labels": [ ... ],    // strand letters, sheet headers, CDR labels
  "number_map": { "13,4": 6547, ... },   // (row,col)  IgStrand number
  "chain": [ [25,1], [24,1], ..., null, ... ],  // ordered cells along chain
  "loop_hints": { ... }       // optional waypoints for ambiguous loops
}

Templates are hand-crafted once per Ig variant (IgV, IgC1, IgC2, IgI, FN3, …) and shipped as JSON in templates/. The repo currently ships IgV-split-AAprime.json; more will come in v0.2.

2. An assignment — maps residues to IgStrand positions

[
  { "residue": "Q",  "igstrand": 1544, "uniprot": 22, "pdb": 1 },
  { "residue": "F",  "igstrand": 1545, "uniprot": 23, "pdb": 2 },
  { "residue": "R",  "igstrand": 1546, "uniprot": 24, "pdb": 3 },
  ...
]

This is the piece iCn3D already produces — see icn3dnode/refnum.js:

node refnum.js [PDB or UniProt IDs] outputs a JSON file containing Ig domain detection, templates, and reference numbers.

The widget consumes a flat list of {residue, igstrand, uniprot, pdb} triplets per residue. A small adapter is all that’s needed to bridge refnum.js output to this format — see the assignment file for the expected shape.

3. Sources (optional metadata)

{
  "uniprot":   { "id": "P01732", "version": 1, "url": "..." },
  "pdb":       { "id": "1CD8", "chain": "A", "url": "..." },
  "alphafold": { "id": "AF-P01732-F1", "version": 4, "url": "..." }
}

Three integration paths

When a user views an Ig domain in iCn3D, surface a link to the public widget:

// In iCn3D, somewhere in the structure-info panel:
const url = `https://vizomics.org/proteomaps?pdb=${pdbId}&chain=${chain}`;
panel.appendChild(makeLink('View 2D Proteomap', url));

The widget at vizomics.org/proteomaps would accept query params and load the appropriate assignment from a static cache (or, in v0.3, compute it live by calling refnum.js on the backend).

Pros: no iCn3D code change. Cons: leaves iCn3D context.

Path B — Embed the widget as an iCn3D panel

iCn3D adds a new dialog/tab “2D Proteomap” that loads the widget inline:

<div id="proteomap-panel"></div>
<script src="https://cdn.jsdelivr.net/gh/vizomics/igfold2d/src/igfold2d.js"></script>
<script>
  // After iCn3D loads structure and computes IgStrand numbering with refnum:
  IgFold2D.render({
    container: '#proteomap-panel',
    template: igfoldTemplate,           // load from /templates/*.json
    assignment: refnumOutput,           // from refnum.js, transformed
    sources: { uniprot: {...}, pdb: {...} }
  });
</script>

The widget has no dependencies — no jQuery, no Three.js, no build step. It exposes a single global IgFold2D (also a CommonJS export) and renders pure SVG into a <div>. Total ~430 lines, ~13 KB minified.

Pros: tight integration; user stays in iCn3D. Cons: iCn3D needs to add a panel and the refnum→assignment adapter.

Path C — Bidirectional sync between iCn3D 3D and IgFold2D 2D

The richest integration: clicking a cell in 2D highlights the residue in 3D (and vice versa), so users navigate the structure through the proteomap.

// Listen to clicks in IgFold2D
proteomapContainer.addEventListener('cellclick', e => {
  const cell = e.detail.cell;
  if (cell.pdb && cell.pdb.pos) {
    // Tell iCn3D to highlight this residue
    icn3dui.icn3d.opts['selection'] = `chain ${cell.pdb.chain} resi ${cell.pdb.pos}`;
    icn3dui.icn3d.draw();
  }
});

// And the reverse: when iCn3D selects residues, highlight matching cells
icn3dui.on('selectionChange', selected => {
  selected.forEach(resi => {
    proteomapWidget.highlight(resi);  // accepts UniProt #, PDB #, IgStrand #, or Excel
  });
});

The widget API supports this natively. See docs/api.md for the full event reference.

For an MVP, Path B is the sweet spot — the iCn3D dev adds one panel, calls refnum.js (already there), passes the output through a small adapter to IgFold2D. Path C can come later by listening to existing iCn3D events.

What’s needed from the iCn3D side

The only piece of glue code:

// Adapter: transform refnum.js output to IgFold2D assignment format
function refnumToAssignment(refnumJson, chain) {
  return refnumJson.domains[0].residues.map(r => ({
    residue: r.aa,           // single-letter amino acid
    igstrand: r.igs,         // IgStrand number
    uniprot: r.uniprot_pos,  // UniProt position
    pdb: r.pdb_pos           // PDB position
  }));
}

The exact field names depend on the current refnum.js output JSON shape — happy to align on naming convention.

Demo & references

Contact

Feedback, issues, integration questions: open an issue at github.com/vizomics/igfold2d/issues or contact Philippe Youkharibache.