Seeded Protein Interaction Network Neighborhood Expansion and Ranking
SPINNER is an interactive local web application for constructing, refining, ranking, and exporting protein interaction networks. It provides a visual workbench around WIPER1/WIPER2 edge-ranking algorithms and real WINNER node scoring.
The algorithm implementation lives in aimed-lab/WIPER. This repository is the separate application layer.
Node scoring is provided by aimed-lab/WINNER.
The default network layout follows the DEMA distance-bounded energy-field
style from aimed-lab/DEMA, with local
force-directed and organic alternatives in the browser.
- Paste or upload tab/comma/whitespace-delimited weighted edge lists.
- Generate Random or scale-free synthetic networks one click; default scale-free graph (34 nodes, 78 edges, w ∈ [0.10, 0.95]) loads on first paint.
- Score edges with WIPER1 or WIPER2 (with optional WIPER1 novel-edge expansion) and nodes with the real WINNER Python package.
- Honor the Iterations input as a hard cap on the force layout — no ticks skipped.
- Google-Maps-style canvas: floating search box top-left, gear + fullscreen top-right,
+ / −zoom stack bottom-right. - Gridded SVG canvas (20 px fine, 100 px major) that pans and zooms with the graph in both light and dark themes.
- Status badge reports
nodes shown / total · edges shown / total. - Force-directed layout reworked for short, balanced edges — weight-scaled springs, soft 1/r² repulsion scaled by node radii, hard collision boost; Tidy (left nav) wipes positions and re-runs the simulation.
- DEMA, force-directed, and organic layouts selectable from the settings popover.
- Edges connected to off-canvas nodes stay visible during pan/zoom (
overflow: visible).
- Map-style search highlights matched nodes/edges with a glow + soft pulse and dims the rest of the network to ~15 %.
- Selection details panel and "Show selection details" pill stay collapsed until the user clicks a node, edge, or search result; details render only the active edge-score metric (Raw / WIPER1 / WIPER2 × UFC / W).
- Maps-style directions card slides down from beneath the search bar with Hops · Raw · W1 · W2 mode tabs (blue underline on active mode).
- From / to inputs flank a vertical rail (origin dot → dotted line → destination square) with a circular swap button; routes auto-compute as inputs change.
- Probabilistic edge weights are −log-converted for path-cost summation; selected route highlights in red, with non-route edges, nodes, and labels dimmed.
- The viewport auto-fits the highlighted path within the unobstructed right side of the canvas.
- Settings popover with: layout (DEMA / Force / Organic); edge score (Raw / WIPER1 / WIPER2 × UFC / W × Linear / Log₂); edges shown (All / Top N / Top % / ≥ score); nodes shown (same); node radius (Linear / Log₂ × Relative / Absolute, plus min / max / × fold).
- Edge widths span an ≈ 11 × ratio with a
t^1.5curve so weak ties stay as hairlines and strong ties read clearly. - Log-scaled WINNER node sizing keeps visible node circles separated; click any node to preview its original-scale circle.
- Right-side slide-over drawer opened from the Assistant nav item; scrim, Esc, and the × button all close it.
- Composer (
⌘ ⏎ to send) for natural-language network construction, ranking, and routing prompts.
- Sticky-header results tables with Edges / Nodes tabs, monospace numerals, row filter, and a left indigo bar on the selected row.
- Export to TSV (shown network, edge table, node table), SVG visualization, Geneterrain, Notion (.md), Word-ready (.doc), and Excel-readable (.xls).
- Client-side
/api/analyzefallback synthesizes plausible WIPER1 / WIPER2 / path-load values when the Python backend is unreachable, so static previews still render a network.
See CHANGELOG.md for the full release notes (current: 1.110).
SPINNER is the integration layer. It does not replace WINNER or WIPER; it coordinates them in one exploratory network workbench.
WINNER ranks genes or proteins in a weighted protein-protein-interaction graph. Given a node list and weighted interactions, WINNER builds a weighted undirected adjacency matrix, computes an initial node score based on weighted degree, and then runs a restart-style network propagation process. Nodes with large final WINNER scores are strongly supported by their local network neighborhood and by paths through high-confidence interactions.
SPINNER imports the real WINNER Python package for node scoring. The network plot uses the final WINNER score to size nodes, while the node table exposes initial and final WINNER values so users can see how much propagation changed each node's rank.
WIPER1 implements the published Weighted In-Path Edge Ranking idea. It first computes optimal weighted paths between nodes, converts those node-to-node relationships into an edge-to-edge traversal graph, initializes edge weights from local edge-network support, and diffuses scores over that edge graph. SPINNER uses WIPER1 when users want behavior close to the original WIPER formulation, including optional novel edge expansion.
WIPER2 is SPINNER's path-aware edge-ranking option. Instead of only asking whether two edges are near each other in an optimal-path matrix, WIPER2 asks which edges are actually traversed by shortest weighted paths. It builds an edge-by-path credit matrix, converts that into an edge co-path graph, and runs WINNER-like restart propagation over edges rather than nodes.
WIPER2 is the default SPINNER edge view because it is usually easier to interpret for backbone discovery: high-ranking edges are those that carry more optimal-path flow, bridge high-confidence neighborhoods, or repeatedly appear in path-supported explanations.
python -m pip install -e .
spinner-web --host 127.0.0.1 --port 8765Then open http://127.0.0.1:8765.
For development against a local WIPER checkout, install WIPER first from its python/ directory:
python -m pip install -e ../WIPER/python
python -m pip install -e .SPINNER expects a tab, comma, or whitespace-delimited edge list. The first two columns are node identifiers and the third column is an edge weight in [0, 1].
node1 node2 weight
A B 0.92
B C 0.88A third-party tool can hand SPINNER a network and let users explore it, or
score a network headlessly. There are two integration surfaces. See
docs/INTEGRATION.md for the full reference, copy-paste
snippets, and the complete response schema.
Open SPINNER's URL with the data attached and it loads + analyzes on boot,
falling back to the demo network if nothing is supplied. Precedence: an
embedded hash payload wins, then ?edges=<url>, then the demo.
| Transport | When to use | Example |
|---|---|---|
?edges=<url> |
Data is hosted somewhere SPINNER can fetch (CORS-enabled) | …/?edges=https://host/net.tsv |
#edges=<base64 TSV> |
Self-contained link, no hosting | …/#edges=QQlCCTAuOQo… |
#text=<URI-encoded TSV> |
Small inline graphs | …/#text=A%09B%090.9%0A… |
#data=<base64 JSON> |
Inline graph plus parameters | …/#data=eyJ0ZXh0Ijoi… |
Optional query parameters work alongside any transport: ?title=<label>
(sets the tab title), ?iterations=<n>, ?novel=1 (include WIPER1 novel
edges). A failed ?edges= fetch falls back to the demo with a chat notice.
The #data= JSON payload accepts: text (required, the edge list),
iterations, includeNovel, device, projectName, projectFolder.
// Build a self-contained deep-link from an in-memory edge list
const tsv = "A\tB\t0.92\nB\tC\t0.88";
const b64 = btoa(unescape(encodeURIComponent(tsv))); // UTF-8 safe
const url = `http://127.0.0.1:8765/#edges=${b64}&` +
new URLSearchParams({ title: "My study", iterations: "80" });
window.open(url, "_blank");const frame = document.getElementById("spinner"); // <iframe src="http://127.0.0.1:8765">
frame.contentWindow.postMessage({
type: "spinner:load",
text: "A\tB\t0.92\nB\tC\t0.88",
iterations: 80,
includeNovel: false,
}, "*");
window.addEventListener("message", (e) => {
if (e.data?.type === "spinner:loaded") console.log("loaded:", e.data.ok);
});POST /api/analyze runs WIPER1, WIPER2, and WINNER and returns JSON — no UI.
All responses send Access-Control-Allow-Origin: *, so browser clients can
call it cross-origin.
curl -s http://127.0.0.1:8765/api/analyze \
-H 'Content-Type: application/json' \
-d '{"text":"A\tB\t0.92\nB\tC\t0.88","iterations":80}'Request body: text (required), iterations (default 80), includeNovel
(default false), device (default "cpu"), maxPathsPerPair (default 1024).
The response has nodes, edges, and a summary carrying
wiper1Available / wiper2Available / winnerAvailable flags and, on dense
graphs where an engine is skipped, a summary.warnings array — the request
still returns 200 with raw edges plus whatever scored. Full schema in
docs/INTEGRATION.md.
SPINNER is free for non-commercial research, education, evaluation, and
academic use. Commercial use requires a separate written license from Dr. Jake
Chen or another authorized copyright holder. See
LICENSE. SPINNER imports the WINNER Python package and the WIPER
package; the WINNER Python port and WIPER2 path-aware edge-ranking variant use
matching non-commercial terms.