2024Libraries
floatnote
Back

floatnote

Transparent always-on-top drawing and note-taking overlay for macOS with annotation tools.

Preview

Point at exactly what you mean

Perfect for code reviews and debugging

Key Features

🎨

Draw Anywhere

Freehand drawing on top of any application. Circles, arrows, text — whatever you need.

👆

Click-Through

The overlay is invisible until you need it. Cmd+Shift+D toggles draw mode.

📸

Screenshot + Annotations

Capture your screen WITH your drawings. Perfect for bug reports.

🎯

Precision Tools

Straight lines with Shift, shapes for highlighting, text for callouts.

🌈

Color Picker

Quick color palette appears when you need it, disappears when you don't.

🖥️

macOS Native

Built with Electron, feels like a native app. Lives in your menu bar.

I was on a video call, trying to explain a UI bug. "No, not that button, the one below it. No, the OTHER one below it. The one that says... no, scroll up... no, too far..."

This happens constantly. Whether it's pair programming, design reviews, or debugging sessions, pointing at things on a shared screen is HARD.

That's when I wished I could just... draw on my screen.

The Idea

An invisible overlay that sits on top of everything. Always on top. When you need to annotate something, you draw on it. When you're done, it disappears.

Like one of those football telestrators, but for your Mac.

Building With Electron (Again)

I know, I know, Electron apps are big. But for this use case, it's perfect. The app is a transparent, frameless, always-on-top window that covers your entire screen. You can click through it when you're not drawing, and it captures input when you are.

The drawing itself is just canvas operations:

  • Freehand drawing with variable stroke width
  • Straight lines (hold Shift)
  • Circles and rectangles (for highlighting)
  • Text annotations
  • An eraser

Colors are picked from a floating palette that appears when you hit a hotkey.

The Transparency Trick

Making an Electron window transparent is easy. Making it CLICK-THROUGH is harder.

The solution is setIgnoreMouseEvents(true, { forward: true }). This tells the window to ignore mouse events but forward them to whatever is below. When you want to draw, you toggle this off.

The toggle is a global hot...

Built With

ElectronJavaScriptmacOS

Impact

npm Downloads

475+

Platform

macOS

Under the Hood

A peek at the implementation — the kind of code that powers floatnote.

overlay.tstypescript
1// The transparency + click-through dance
2// This is the magic that makes floatnote work
3
4import { BrowserWindow, globalShortcut, screen } from 'electron';
5
6let overlayWindow: BrowserWindow | null = null;
7let isDrawMode = false;
8
9function createOverlay() {
10  const { width, height } = screen.getPrimaryDisplay().workAreaSize;
11
12  overlayWindow = new BrowserWindow({
13    width,
14    height,
15    x: 0,
16    y: 0,
17    transparent: true,           // See-through
18    frame: false,                // No window chrome
19    alwaysOnTop: true,           // Always visible
20    skipTaskbar: true,           // Don't show in dock
21    hasShadow: false,            // No shadow (it's invisible!)
22    webPreferences: {
23      nodeIntegration: true,
24      contextIsolation: false,
25    },
26  });
27
28  // Start in click-through mode
29  overlayWindow.setIgnoreMouseEvents(true, { forward: true });
30
31  overlayWindow.loadFile('overlay.html');
32
33  // Global hotkey to toggle draw mode
34  globalShortcut.register('CommandOrControl+Shift+D', toggleDrawMode);
35}
36
37function toggleDrawMode() {
38  isDrawMode = !isDrawMode;
39
40  if (isDrawMode) {
41    // Capture mouse events - we're drawing now
42    overlayWindow?.setIgnoreMouseEvents(false);
43    overlayWindow?.webContents.send('draw-mode', true);
44    
45    // Show the toolbar
46    overlayWindow?.webContents.send('show-toolbar');
47  } else {
48    // Click-through mode - window is invisible again
49    overlayWindow?.setIgnoreMouseEvents(true, { forward: true });
50    overlayWindow?.webContents.send('draw-mode', false);
51  }
52}
53
54// Screenshot with annotations
55async function captureWithAnnotations(): Promise<Buffer> {
56  // Temporarily hide the toolbar
57  overlayWindow?.webContents.send('hide-toolbar');
58  
59  await sleep(100); // Wait for render
60  
61  // Capture the SCREEN (not the window)
62  const sources = await desktopCapturer.getSources({
63    types: ['screen'],
64    thumbnailSize: screen.getPrimaryDisplay().size
65  });
66  
67  const screenCapture = sources[0].thumbnail;
68  
69  // Now capture our overlay
70  const overlayCapture = await overlayWindow?.webContents.capturePage();
71  
72  // Composite them together
73  const combined = await compositeImages(screenCapture, overlayCapture);
74  
75  overlayWindow?.webContents.send('show-toolbar');
76  
77  return combined.toPNG();
78}

Discussion

💬 Be nice, or be funny. Preferably both.