Building a Privacy-First Project Manager in the Browser
When we set out to build Vedratic Project Manager, we had a clear thesis: project planning tools should be fast, free, and private. Most existing Gantt chart editors fall into one of two categories — enterprise behemoths that require accounts, subscriptions, and cloud storage, or toy-like apps that lack the depth to handle real project complexity. We wanted to build something in the middle: a professional-grade visual planner that runs entirely in your browser with zero backend dependencies.
This article is a technical deep-dive into how we architected the application, the trade-offs we made, and the technologies that power it.
Architecture Overview
Vedratic Project Manager is a single-page application (SPA) built with React 19, TypeScript, and Vite. Unlike Globdrop (our geography game, which uses vanilla TypeScript for maximum frame-rate performance), Project Manager benefits from React's component model because it's a form-heavy, state-intensive CRUD application where developer productivity and maintainability matter more than raw render speed.
The architecture follows a clean separation:
📦 State Layer
React Context + useReducer for project state. All mutations flow through a single dispatcher, making state transitions predictable and debuggable.
💾 Persistence Layer
localStorage with JSON serialization. Projects auto-save on every state change with debounced writes to avoid performance issues.
📊 Rendering Layer
Mermaid.js for diagram rendering, plus custom SVG generation for high-fidelity exports that preserve styling and layout.
🖥️ View Layer
Four distinct views — Interactive Gantt, Deliverables, Resources, and Mermaid Preview — all sharing the same underlying project model.
Why Zero Backend?
The decision to build a fully client-side application was deliberate and philosophically motivated. We believe that a project planning tool handles some of the most sensitive business data a team produces: timelines, resource allocation, strategic priorities, and deadline commitments. Sending this data to a third-party server introduces risk — not just from breaches, but from the vendor's own data practices, analytics pipelines, and business model changes.
By storing everything in the browser's localStorage, we guarantee:
- Complete privacy: Your project data never leaves your device. We cannot see it, cannot sell it, and cannot lose it in a breach because we never have it.
- Offline capability: The app works without an internet connection after the initial load. Plan on a flight, in a café without Wi-Fi, or in a restricted corporate network.
- Zero latency: Reads and writes to localStorage are synchronous and sub-millisecond. There's no spinner waiting for a server response. Every interaction feels instant.
- No accounts: No sign-up flow, no password management, no email verification. Open the URL and start working immediately.
The localStorage Trade-off
Of course, localStorage isn't perfect. The primary limitations are:
Storage Limits
Most browsers cap localStorage at 5-10 MB per origin. A single project with 50 tasks, 10 team members, and full configuration weighs roughly 15-25 KB in JSON. This means you can comfortably store hundreds of projects before approaching the limit. For typical use, this is more than sufficient.
No Cross-Device Sync
Since data lives only in the browser where it was created, you can't seamlessly switch between your laptop and desktop. Our solution is JSON export/import: export your project as a .json file, transfer it via any file-sharing mechanism (email, Dropbox, USB drive), and import it on the other device. This is manual but deliberately so — it keeps the user in full control of their data's movement.
Clearing Browser Data
If a user clears their browser's localStorage (or uses a browser/extension that aggressively manages storage), their projects are gone. We mitigate this with prominent export reminders and by making the export flow as frictionless as possible — a single click generates and downloads the JSON file.
Design Principle: Explicit Data Portability
We intentionally chose not to add cloud sync as a "convenience" feature. Cloud sync creates vendor lock-in, requires account creation, and fundamentally changes the privacy model. Instead, we invested in making export/import seamless across three formats: JSON (for backup and sharing), Mermaid .mmd (for documentation embedding), and SVG (for presentations). The user owns their data in standard, open formats.
The Gantt Calculation Engine
The core algorithmic challenge of a Gantt chart editor is date calculation with dependencies. When a user defines that Task B depends on Task A, Task B's start date must be automatically computed as Task A's end date plus any buffer. If Task A's duration changes, Task B (and everything downstream) must recalculate.
Our engine uses a topological sort of the task dependency graph followed by a forward pass to compute earliest start dates. The algorithm runs in O(V + E) time, where V is the number of tasks and E is the number of dependency edges. For a project with 100 tasks and 80 dependencies, this completes in under 1ms — well within a single animation frame.
// Simplified dependency resolution
function resolveSchedule(tasks: Task[], dependencies: Dependency[]): ScheduledTask[] {
// Build adjacency list and in-degree map
const adj = new Map<string, string[]>();
const inDegree = new Map<string, number>();
tasks.forEach(t => {
adj.set(t.id, []);
inDegree.set(t.id, 0);
});
dependencies.forEach(d => {
adj.get(d.from)!.push(d.to);
inDegree.set(d.to, (inDegree.get(d.to) || 0) + 1);
});
// Kahn's algorithm for topological order
const queue: string[] = [];
inDegree.forEach((deg, id) => { if (deg === 0) queue.push(id); });
const order: string[] = [];
while (queue.length > 0) {
const current = queue.shift()!;
order.push(current);
for (const neighbor of adj.get(current)!) {
const newDeg = inDegree.get(neighbor)! - 1;
inDegree.set(neighbor, newDeg);
if (newDeg === 0) queue.push(neighbor);
}
}
// Forward pass: compute start dates
const scheduled = new Map<string, Date>();
for (const taskId of order) {
const task = tasks.find(t => t.id === taskId)!;
const predecessors = dependencies
.filter(d => d.to === taskId)
.map(d => addDays(scheduled.get(d.from)!, getEffectiveDuration(tasks.find(t => t.id === d.from)!)));
scheduled.set(taskId, predecessors.length > 0
? new Date(Math.max(...predecessors.map(d => d.getTime())))
: task.manualStartDate || projectStartDate);
}
return order.map(id => ({ ...tasks.find(t => t.id === id)!, startDate: scheduled.get(id)! }));
} The getEffectiveDuration function is where our buffer and validation complexity features plug in. The effective calendar duration of a task is calculated as:
effectiveDuration = timespan + buffer + (effort × validationComplexity) This formula accounts for the raw calendar allocation (timespan), safety margin (buffer), and review overhead (validation complexity applied to the actual effort estimate). The result is a schedule that bakes in realistic expectations from the start.
Mermaid.js Integration
One of the most popular features of Vedratic Project Manager is Mermaid export. Mermaid.js is a JavaScript-based diagramming library that renders charts from a simple text-based syntax. GitHub, GitLab, Notion, and many other platforms render Mermaid natively, making it the de facto standard for embedding diagrams in documentation.
Our Mermaid view generates valid Gantt chart syntax from the project model in real-time. As you add tasks, change durations, or modify dependencies in the Interactive view, the Mermaid code updates instantly. You can copy the raw code or download it as a .mmd file for embedding.
The reverse is also supported: you can import existing Mermaid Gantt charts. The parser handles sections, task definitions, dependency markers (after keywords), and status flags (done, active, crit). This makes Vedratic interoperable with any workflow that already uses Mermaid for project visualization.
SVG Export: Pixel-Perfect Charts
While Mermaid is great for documentation, it doesn't give you full control over styling. For presentations, reports, and client deliverables, you need a polished, branded chart. Our SVG export engine renders the Gantt chart as a high-fidelity vector graphic with:
- Custom color coding per task type (Design, Development, Testing, etc.).
- Dependency arrows with proper routing to avoid overlaps.
- Phase groupings with labeled section headers.
- A time axis with configurable granularity (days or months).
The SVG is generated entirely client-side using DOM APIs. We create an off-screen SVG element, populate it with <rect>, <text>, <line>, and <path> elements, serialize it to a string, and trigger a download. No canvas, no external rendering service, no server round-trip. The result is a crisp, infinitely scalable vector file that looks perfect at any size.
Performance Considerations
Even though Project Manager is a React application (and thus carries more runtime overhead than our vanilla-JS Globdrop game), we've been careful to keep it fast:
- Memoization: Expensive computations like dependency resolution and Mermaid code generation are memoized with
useMemo, only recalculating when the dependency graph actually changes. - Debounced persistence: localStorage writes are debounced to 300ms, preventing disk thrashing during rapid edits like dragging a task bar.
- Lazy rendering: The Mermaid preview and SVG export views don't render until activated, keeping the initial Interactive view fast.
- Vite bundling: Tree-shaking and code splitting keep the production bundle lean. The entire application loads in under 200KB gzipped.
What's Next
We're actively working on several improvements to Project Manager:
- Critical Path Highlighting: Visual emphasis on the longest dependency chain, showing which tasks directly determine the project's end date.
- Drag-to-Resize Tasks: Adjust task duration by dragging the edges of task bars directly on the Gantt chart.
- Template Library: Pre-built project templates for common workflows like software sprints, marketing campaigns, and product launches.
- PDF Export: One-click generation of print-ready project plans with cover pages and summary statistics.
If you haven't tried it yet, launch Vedratic Project Manager and start planning your next project. It's free, private, and takes less than a minute to learn.