Logo

Command Palette

Search for a command to run...

Notra

Notra: Building a Free-Form Canvas Desktop Note-Taking Client

Most note-taking applications enforce a strict, vertical grid. You write from top to bottom, paragraph by paragraph. But when we brain-storm or plan projects, our thoughts aren't linear. We map things out, draw lines, and position ideas relatively.

That is why I built Notra—a desktop-only, block-based note-taking application that replaces the linear editor with an interactive, free-form spatial canvas.

dYO? Visit the repository


💡 The Core Concept

The vision was simple: create an infinite scrolling workspace (CANVAS_W = 4000, CANVAS_H = 3000) where blocks are standalone nodes. You can position them anywhere, drag them, resize them, and rotate them.

Every block can contain:

  • Rich text paragraphs & headings
  • Interactive checklist items
  • Hand-drawn canvases and sketches
  • Audio/voice recordings and media cards

🎨 Design & Interaction

To make spatial note-taking feel natural, the interaction design had to be completely lag-free:

  • Spatial Control: Every block features custom drag handles, resize corners, and a top rotation knob.
  • Visual Clarity: Used glassmorphic container headers with dynamic neon outlines to emphasize selection.
  • Double-Click Operations: Shift+clicking anywhere on the canvas instantly generates a new text block at those coordinate offsets.
  • Markdown Conversions: Real-time listeners monitor typing patterns to translate inline characters into headings or lists automatically (e.g. typing # transforms the block type instantly).

🛠️ The Tech Stack

I chose a cross-platform desktop stack focused on low-latency rendering and robust local-first storage.

Desktop & Core Framework

  • React Native Web + Electron: Employs React Native's core layout models running inside an Electron desktop window context.
  • Zustand: Manages global active note selections, settings, and workspace settings.

Local Database

  • SQLite (react-native-sqlite-storage): Handles local storage with relational referential integrity. Deleting a note automatically deletes all associated coordinate block models (ON DELETE CASCADE).

🚀 Key Engineering Challenges

Building an interactive canvas in React Native Web poses major performance hurdles:

Decoupling Interaction State from Database Writes

Initially, updating coordinates in the database on every pixel of a drag caused massive lag due to disk write queues. I solved this by introducing local React component state for active drag/resize coordinates. The layout renders from local states during the gesture. Once the user releases the mouse, the final coordinate is flushed to the Zustand store and written to SQLite, maintaining a solid 60fps interaction speed.

Handle Overflow and Clipping

Electron default styles wrap web views tightly. Because selection corners and rotate knobs lie outside the block bounds, standard layouts clipped them. By setting the container inline style specifically to overflow: 'visible', I ensured selections are fully interactable outside the text fields.


🎓 What I Learned

This project pushed me to think deeply about local-first architectures and state-driven animations. I improved my skills in:

  • De-coupling high-frequency rendering interactions from database persistence.
  • Configuring robust SQLite schemas with clean migrations.
  • Customizing Electron window frames, title bar shortcuts, and native system shortcuts.
  • Building custom gesture handlers and interactive DOM controls.