Skip to content

AlbertoBarrago/Markasso

Repository files navigation

Markasso logo

MARKASSO

is a zero-latency, framework-agnostic whiteboard engine. Built with vanilla TypeScript and the Canvas 2D API, it delivers a high-performance drawing experience with zero dependencies and a keyboard-first workflow. It’s the "Vesper" of whiteboards: dark, fast, and focused.

Live Demo



TypeScript Vite Vitest Zero dependencies License


User Manual · Guide · Contributing · Report an Issue


Overview

Markasso is a fast, minimal, keyboard-first whiteboard engine that runs entirely in the browser.

Built with vanilla TypeScript and the Canvas 2D API — no framework dependencies. Just you, a canvas, and JavaScript doing exactly what it was invented to do.

This repository is the source of truth for the production deployment.

While Excalidraw excels at freehand sketching and Draw.io offers comprehensive diagramming, Markasso occupies a different niche: smaller, faster, and requiring no sign-in, cookies, or workspace creation.


Motivation

Markasso was born from a simple frustration: wanting to sketch a quick diagram shouldn't require:

  1. Waiting for a heavy application to load
  2. Dismissing cookie consent banners
  3. Creating an account for "free" features
  4. Risking lost work due to forgotten exports

Markasso is the alternative. Open, draw, export, done.


Features

Drawing Tools

Tool Shortcut
Hand H / Space
Select V / 1
Rectangle R / 2
Rhombus (Diamond) D / 3
Ellipse E / 4
Line + arrowheads A / L / 5
Curve (bezier) C
Polygon / Polyline O
Pen (freehand) P / 7
Text T / 8
Eraser 0

Canvas & Navigation

Feature Description
Infinite canvas Pan with middle-click or Alt+drag; zoom with Ctrl+scroll
Millimeter grid Dot · Line · Graph-paper modes (real mm at 96 DPI)
Navigation recovery F fits all content into view · Shift+0 resets to origin
Dark / Light theme Switchable themes with system preference support

Selection & Manipulation

Feature Description
Resize handles 8-handle bounding box on every element type
Rotation Rotate any element via a handle above the selection box (fully undoable)
Endpoint editing Drag individual endpoints of lines and arrows independently
Shift+click multi-select Add or remove elements from selection without a marquee
Arrow key nudge Move selected elements 1px (or 10px with Shift)
Lock elements Lock any element to prevent selection, movement, or deletion

Smart Connections

Feature Description
Smart arrow links Connect arrows to shapes — arrows attach to the border (not center), facing each other
Hover preview Hover to preview connection points before drawing
Cascade delete Deleting a shape removes all connected arrows and lines

Groups & Labels

Feature Description
Groups Ctrl+G to group · click selects all members · click again enters group for individual editing
Shape labels Double-click any rectangle or ellipse to type a label, clipped inside the shape
Connector labels Add labels to lines with arrowheads and keep the gap aligned to the bezier bend
Text scaling Dragging a text handle scales the font size, not just the box
Double-click to edit Open any existing text element for inline editing

Constraints & Precision

Feature Description
Shift constraints Rectangle/Ellipse → square/circle · Line/Arrow → 45° snap · Resize → keep aspect ratio
Hover highlight Elements highlight on hover — always know what you're about to select
Eraser hover Elements highlight as you drag the eraser over them

UI & Interface

Feature Description
Floating glass UI Excalidraw-style islands: lock · hand · tools · eraser, top-right import + export; mobile gets a compact bottom-right action bar
Properties panel Stroke color, fill color, stroke width, stroke style, linecap icons, opacity, roughness, shadow, rounded corners; font size + family + bold/italic/underline/strikethrough for text
Tool lock Lock button keeps the active drawing tool after placing a shape instead of reverting to Select
Panel toggle \ (backslash) shows/hides all UI panels for a distraction-free canvas
Command palette Ctrl+K — fuzzy-search and run any action without touching the mouse
Element search Ctrl+F — search elements by label/content, click to pan and select
Minimap Collapsible canvas overview; click or drag to pan the viewport
Share link Encode the full scene into a URL hash for instant one-click sharing
Keyboard navigation Full Tab-based keyboard navigation through all panels
About modal Info panel with version, links, and credits

Persistence & Export

Feature Description
Session persistence Auto-saved to localStorage — your work survives page refreshes
.markasso format Save and reload your full scene as a .markasso file (JSON) — images included
Image import Drag-and-drop, file picker, or Ctrl+V paste; .markasso files can also be dropped directly
Mermaid import Import .mmd / .mermaid files via drag-and-drop or the toolbar button; paste Mermaid text from the clipboard — graph, flowchart, and sequenceDiagram are converted to native elements
Export PNG / SVG / HTML Download as 2× PNG, clean SVG, or standalone HTML — all respect the current canvas background color
Share link One-click URL encoding of the full scene — no server, no account

History & Undo

Feature Description
Undo / Redo Full command history with Ctrl+Z / Ctrl+Y or Ctrl+Shift+Z

Performance

Feature Description
Zero dependencies Browser Canvas 2D API only — no React, no framework, no bundled megabytes

Getting Started

Install

pnpm install

Develop

pnpm dev        # Start dev server at http://localhost:5173

Build & Test

pnpm build      # Type-check and bundle → dist/
pnpm typecheck  # TypeScript validation
pnpm test       # Run Vitest unit tests

No .env files. No API keys. No containerization required.


Keyboard Shortcuts

Tools

Key Action
H / Space Hand (pan)
V / 1 Select
R / 2 Rectangle
D / 3 Rhombus (Diamond)
E / 4 Ellipse
A / L / 5 Line + arrowheads
C Curve
O Polygon
P / 7 Pen (freehand)
T / 8 Text
N Sticky note
0 Eraser

Navigation

Key Action
G Toggle grid
F Fit all content into view
Shift+0 Reset viewport to origin
\ Toggle all UI panels
Scroll Pan
Middle-click drag / Alt+drag Pan
Ctrl+scroll Zoom to cursor

Search & Commands

Key Action
Ctrl+K Open command palette
Ctrl+F Open element search

Editing

Key Action
Esc Cancel / exit group / deselect
Delete / Backspace Delete selected elements
Arrow keys Nudge selection 1px
Shift+Arrow Nudge selection 10px
Ctrl+A Select all
Ctrl+D Duplicate selection
Ctrl+G Group selected elements
Ctrl+Shift+G Ungroup
Ctrl+Shift+] Bring to front
Ctrl+Shift+[ Send to back
Ctrl+Z Undo
Ctrl+Y / Ctrl+Shift+Z Redo
Shift+click Add / remove from selection

Modifiers

Key Action
Shift (while drawing) Constrain proportions / snap angle
Shift (while resizing) Lock aspect ratio
Double-click on text Edit text in place
Double-click on rect / ellipse Edit shape label
Text tool Click to place · drag to define area · Enter = commit · Shift+Enter = newline · Esc = cancel

Architecture

Markasso follows a Redux-style unidirectional data flow — no mutable state, no event spaghetti, no surprises.

User event
    │
    ▼
Tool (onMouseDown / onMouseMove / onMouseUp)
    │  dispatches Command
    ▼
History.dispatch(command)
    │  calls
    ▼
reducer(Scene, Command) → Scene   ← pure function, no side effects
    │  notifies
    ▼
Subscribers (toolbar, properties panel, canvas view)
    │
    ▼
render(ctx, scene, canvas)        ← called every requestAnimationFrame

Key Invariants

  • All coordinates in CSS pixels. The canvas buffer is clientWidth × devicePixelRatio for sharpness, but all viewport offsetX/Y, element positions, and mouse events live in CSS pixel space.
  • Immutable scene. Every Scene object is never mutated. The reducer returns a new reference or the same reference if nothing changed (enabling cheap equality checks).
  • Ephemeral commands (pan, zoom, set-viewport, select, tool change) are excluded from the undo stack.
  • APPLY_STYLE is the single undoable command that updates both appState defaults and all currently selected elements in one atomic operation.

Project Structure

src/
├── core/
│   ├── scene.ts            # Scene interface + factory
│   ├── viewport.ts         # Pan/zoom math (screenToWorld, worldToScreen)
│   └── app_state.ts        # AppState (activeTool, colors, grid, …)
├── elements/
│   └── element.ts          # Discriminated union for all element types
├── commands/
│   └── commands.ts         # Full Command discriminated union
├── engine/
│   ├── reducer.ts          # Pure (Scene, Command) → Scene
│   └── history.ts          # Undo/redo stack + pub/sub
├── io/
│   ├── markasso.ts         # .markasso save / load (exportMarkasso, importMarkasso)
│   ├── mermaid.ts          # Mermaid parser + layout engine → Markasso elements
│   ├── session.ts          # localStorage auto-save / restore + quota warning toast
│   └── share.ts            # URL-hash scene encoding / decoding for share links
├── rendering/
│   ├── renderer.ts         # rAF render loop entry point
│   ├── draw_element.ts     # Per-type draw dispatch (with rotation + shape labels)
│   ├── draw_grid.ts        # Dot / line / mm graph-paper grids
│   ├── draw_selection.ts   # Selection box, resize/rotation/endpoint handles + hit testing
│   └── export.ts           # exportPNG / exportSVG / exportHTML (bounding-box auto-fit)
├── tools/
│   ├── tool.ts             # Tool interface
│   ├── select_tool.ts      # Hit test, marquee, drag-move, resize
│   ├── rectangle_tool.ts
│   ├── ellipse_tool.ts
│   ├── rombo_tool.ts       # Rhombus tool
│   ├── line_tool.ts        # Line tool; arrowheads are a style on line elements
│   ├── pen_tool.ts         # Freehand with Catmull-Rom smoothing + stylus pressure
│   ├── text_tool.ts        # Invisible overlay textarea + in-place editing
│   └── sticky_tool.ts      # Sticky note with editable text and color picker
└── ui/
    ├── canvas_view.ts       # DOM event hub → world coords → tool
    ├── toolbar.ts           # Floating islands: tools · undo/zoom · import · export · share
    ├── context_panel.ts     # Properties panel (style, formatting, shadow, alignment)
    ├── command_palette.ts   # Ctrl+K fuzzy-search command launcher
    ├── element_search.ts    # Ctrl+F canvas element search overlay
    ├── minimap.ts           # Collapsible canvas overview + viewport panning
    ├── image_import.ts      # File picker, drag-and-drop, paste — images + .markasso
    ├── settings.ts          # Hamburger panel: grid, accent color, theme, language
    ├── mobile_action_bar.ts # Compact bottom-right action bar for touch devices
    └── shortcuts.ts         # Global keyboard map

Grid Modes

Mode Description
Dot Subtle dots at configurable world-unit spacing
Line Horizontal + vertical lines
mm Three-tier graph paper (1 mm / 5 mm / 10 mm) at physical scale assuming 96 DPI

Technology Stack

Concern Choice Rationale
Language TypeScript 5 Exhaustive switch on discriminated unions catches unhandled commands at compile time
Build Vite Zero-config, instant HMR, single-file output
Rendering Canvas 2D API Direct pixel control without virtual DOM overhead
Testing Vitest Runs in Node — no browser needed for reducer/viewport math
Dependencies None crypto.randomUUID(), requestAnimationFrame, ResizeObserver — all native

Contributing

See CONTRIBUTORS.md for guidelines.

License

MIT — © 2026 Alberto Barrago

Draw things. Ship things. Touch grass.

About

A fast, minimal, keyboard-first whiteboard engine for the browser. Marker + Picasso. No framework. No runtime. Just canvas.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors