← Projects
Active Research

Phenomenal

View on GitHub ↗ Live Demo →
Node.js + Expressbetter-sqlite3 (SQLite)WebSockets (ws)D3 v7 (CDN, no build)Pure HTML/CSS/JSNVivo-compatible export

About this project

Self-hosted qualitative research platform for thematic coding, grounded theory analysis, and co-occurrence visualisation. Pure HTML/CSS/JS frontend with no build step — text selection maps directly to char offsets, codes are applied in-browser, and D3 v7 renders a live force graph of code co-occurrence across transcripts. SQLite backend, zero config.

Background

I had 60 interview transcripts and needed a way to code them that didn't involve sending research data to a cloud service or paying for NVivo licences. The problem with most qualitative research tools is that they're either expensive, require institutional IT involvement to deploy, or both. I wanted something I could run on a server I controlled, share with supervisors via a URL, and own completely.

The core interaction — select text, apply a code — sounds simple but requires some care to get right in a browser. I used mouseup events with window.getSelection() to capture selection boundaries, then mapped them to character offsets within the transcript text. That offset pair is what gets stored, which means the same segment can carry multiple codes and the data remains stable if you edit the display layer. No React, no build step — the entire frontend is plain HTML, CSS, and vanilla JavaScript, which means it works anywhere Node.js runs without any toolchain friction.

The co-occurrence graph was the feature that made the tool genuinely analytical rather than just a bookmarking system. When you code 60 interviews, patterns emerge in the graph before you've consciously noticed them in the text. Nodes that cluster tightly together are signalling a theme — D3's force simulation makes that visible. Node size encodes segment count, edge thickness encodes co-occurrence weight, and dragging nodes to reposition them lets you explore the structure without changing the data.

Memos complete the loop: the graph surfaces a pattern, you write a memo while it's fresh, and that memo becomes the first draft of the theoretical chapter. The export to NVivo-compatible format was added for collaborators who needed to import my coding work into their existing environment. The platform is deployed on Fly.io on the free tier — it sleeps between sessions, which is fine for the usage pattern.

Highlights

  • mouseup + getSelection + char offsets for in-browser text coding without any framework
  • D3 v7 force graph: nodes = codes, edges = co-occurrence weight across transcripts
  • SQLite schema: transcripts → segments → codes, with memos linked to either
  • Export to CSV, JSON, and NVivo-compatible tab-delimited format in one click
  • WebSocket connection for real-time multi-user coding sessions
← All projects GitHub ↗
← Papertrail Enterprise Observability Stack →