Skip to content

Repository files navigation

Shotmark β€” Lightweight Web Screenshot Annotation Tool

Shotmark - Capture Β· Draw Β· Copy Β· Download

npm version license bundle size demo

One line to start. 10 annotation tools. Everything runs client-side β€” no server uploads, total privacy.

Live Demo Β· δΈ­ζ–‡ζ–‡ζ‘£


πŸ“– Table of Contents

  1. Sound Familiar?
  2. What is Shotmark?
  3. Three Steps to Annotate
  4. 10 Built-in Tools
  5. Six Core Highlights
  6. Architecture
  7. Quick Start
  8. Full API Reference
  9. FAQ
  10. Use Cases
  11. More Resources

01 Sound Familiar?

Before diving into the API, ask yourself:

  • πŸ› Bug reports: Your tester screenshots a bug and wants to circle the problem β€” but there's no annotation tool in the browser. Time to switch to Snipaste, alt-tab back and forth...
  • πŸ“± Privacy leaks: You share a screenshot only to realize a phone number or order ID is visible. Recall message... too late, someone saved it.
  • πŸ“ Pixel measuring: UI review means opening DevTools and clicking elements one by one. A full page later, your wrist is dead.
  • πŸ“ Tutorial creation: Writing docs with step numbers and arrows? Spend more time fighting your screenshot tool than writing the actual guide.
  • πŸ”€ Window-switching hell: Screenshot β†’ open annotation app β†’ annotate β†’ save β†’ switch back β†’ upload. That's 4 context switches for one image.

If any of these hit home, Shotmark is for you.

Pain Points


02 What is Shotmark?

In one sentence: capture, annotate, and export screenshots β€” all inside the browser.

No desktop app install. No leaving the browser. No complex config. Import Shotmark, wire up a button, and your users can enter screenshot mode β€” draw rectangles, arrows, text, mosaic, then copy or download in one click.

Core traits:

  • πŸ“¦ Lightweight β€” ~113 kB (gzip β‰ˆ 35 kB), only 2 runtime dependencies
  • πŸ”’ Private β€” Everything runs client-side, images never leave the browser
  • 🎨 Rich β€” 10 annotation tools covering every scenario
  • ⚑ Simple β€” One line to start, 5 minutes to integrate
  • πŸŒ— Polished β€” Light/dark themes, English & Chinese i18n
  • 🧩 Flexible β€” Tools, buttons, and colors are fully configurable

03 Three Steps to Annotate

Whether you're a developer, tester, designer, or PM β€” the workflow is the same:

Workflow

Step 1: Launch

Add a trigger button that calls Shotmark.start(). Click it β€” the page enters capture mode.

Step 2: Annotate

Pick a tool (rectangle, arrow, text, mosaic…) and draw freely. Made a mistake? Ctrl+Z to undo, unlimited.

Step 3: Export

Three ways out:

  • πŸ“‹ Copy to clipboard β€” Ctrl+V straight into chat or docs
  • πŸ’Ύ Download file β€” Save as PNG or JPEG locally
  • πŸ”— base64 callback β€” Your code gets the image data to upload programmatically

04 10 Built-in Tools

"So many tools β€” what does each one do?" Here's the cheat sheet:

Tools

Tool Icon Purpose Pro tip
Rectangle β–‘ Highlight a region Hold Shift for a perfect square
Ellipse β—‹ Circle irregular areas Hold Shift for a perfect circle
Arrow β†’ "Look here!" pointer Drag endpoints to adjust
Brush ✐ Freehand drawing Strokes auto-smooth
Text T Add text labels Multi-line, resizable font & color
Mosaic β–¦ Redact private info Adjustable granularity & softness
Highlight ━ Fluorescent marker Semi-transparent, doesn't obscure
Number β‘  Step numbering Auto-increment, configurable start
Line -- Underline / divider Shift for horizontal or vertical
Measure ↔ Pixel distance Shows px value β€” UI review superpower

Universal actions:

  • πŸ”„ Undo / Redo β€” Ctrl+Z / Ctrl+Shift+Z (Mac: Cmd)
  • 🎯 Select & adjust β€” Click a shape, drag anchors to resize/move
  • 🎨 Restyle live β€” Change color, stroke width, font size on selected shapes
  • πŸ–±οΈ Scroll wheel β€” Quickly adjust line width or font size

⌨️ Keyboard Shortcuts

Master these and your annotation speed doubles:

Shortcut Action
1 – 0 Switch to Nth tool (toolbar order)
Ctrl/Cmd + Z Undo
Ctrl/Cmd + Shift + Z Redo
Ctrl/Cmd + C Copy screenshot to clipboard
Delete / Backspace Delete selected shape
Enter Confirm and export
Escape Close annotation
Shift + draw Constrain axis (square / circle / H / V line)
Scroll wheel Adjust line width or font size

πŸ’‘ Number keys work on both the main keyboard and numpad, and are IME-safe β€” they work even with Chinese/Japanese input active.


05 Six Core Highlights

Highlights

⚑ One Line Setup

Shotmark.start({ region: document.getElementById('target') });

No new, no config file, no lifecycle hooks. One function call does it all. On close, Shotmark auto-cleans its DOM β€” zero leftovers.

πŸ”’ Data Never Leaves the Browser

From capture to export, everything stays in browser memory:

πŸ–₯️ Page DOM β†’ πŸ“Έ Canvas pixel grab β†’ ✏️ SVG annotation β†’ 🎨 Canvas composite β†’ πŸ–ΌοΈ Image output

Zero network requests. No third-party services. The screenshot only exists in the onShot callback parameter β€” if you don't upload it, it stays in memory until garbage collected.

πŸ“¦ 113 kB, Zero UI Dependency

  • Bundle β‰ˆ 113 kB (gzip β‰ˆ 35 kB)
  • Runtime deps: only @emotion/css + clsx
  • No dependency on any UI library (antd / element / material β€” none)
  • React β‰₯ 17.0.0 (supports 17 and 18)

πŸŒ— Follows Your System

Built-in light / dark theme switching. Plus zh-CN / en-US i18n with custom text overrides for full localization control.

🧩 Show Only What You Need

// Bug reporting: just 4 tools
Shotmark.start({
  tools: ['rectangle', 'arrow', 'text', 'mosaic'],
  actions: ['cancel', 'copy'],
});

// UI review: measurement first
Shotmark.start({
  tools: ['measure', 'rectangle', 'arrow', 'text'],
  defaultTool: 'measure',
  actions: ['cancel', 'download'],
});

The toolbar shows only what you configure β€” clean and focused.

πŸ–ΌοΈ CSS Background Images? No Problem.

Most web screenshot solutions break on background-image β€” mosaic renders blank, or backgrounds vanish on export.

Shotmark handles full background image rendering during rasterization: cover, contain, repeat, multi-backgrounds, gradient + image combos β€” all preserved in the exported image.


06 Architecture

πŸ’‘ Curious about the internals? This section covers how Shotmark works under the hood. If you just want to integrate, skip to Quick Start.

Architecture

Shotmark uses a five-layer separation architecture:

Layer Module Responsibility
L1 Controller Config validation, lifecycle management
L2 Selection Layer Region picking, overlay mask, drag-to-adjust
L3 Draw Board SVG drawing, shape management, undo/redo
L4 Toolbar Tool switching, color/width/font config
L5 Rasterizer DOM β†’ SVG β†’ Canvas β†’ Image

Interesting technical details:

  1. Plugin-based shapes β€” Each tool is an independent GraphPlugin implementing a down β†’ move β†’ up three-phase protocol. Adding a new shape means registering one file β€” no other code touched.

  2. True pixel mosaic β€” Not a CSS filter: blur() hack. Raw pixels are grabbed from Canvas, block-averaged, and painted back β€” identical to Photoshop's pixelate.

  3. Double-rAF export β€” On submit, a double requestAnimationFrame strategy lets the UI animation render one frame before the heavy rasterization task runs β€” users feel zero jank.

  4. Config memory β€” Colors, stroke widths, and font sizes persist across sessions β€” next time you open Shotmark, your preferences are restored.


07 Quick Start

5 minutes to integrate.

Step 1: Install

pnpm add shotmark
# or: npm i shotmark / yarn add shotmark

react and react-dom are peer dependencies (β‰₯17). Install them in your host project.

Step 2: Import & Call

import Shotmark from 'shotmark';

function ScreenshotButton() {
  const handleClick = () => {
    const el = document.getElementById('target-area');
    if (!el) return;

    Shotmark.start({
      region: el, // what to capture
      autoAnnotate: true, // enter annotation mode immediately
      onShot: (result) => {
        // result.image is the annotated base64 image
        console.log('Done:', result.width, 'Γ—', result.height);
      },
    });
  };

  return <button onClick={handleClick}>πŸ“Έ Screenshot</button>;
}

Step 3: There Is No Step 3

Seriously. Shotmark.start() opens it, the user annotates, and it auto-closes with DOM cleanup. Need programmatic close? Call Shotmark.close().

Advanced Usage

Free selection mode (no region β€” user drags their own area):

Shotmark.start({
  onShot: (result) => uploadToServer(result.image),
});

Custom tools + dark theme:

Shotmark.start({
  region: el,
  tools: ['rectangle', 'arrow', 'text', 'mosaic'],
  actions: ['cancel', 'copy', 'download'],
  theme: 'dark',
  defaultColor: '#ff4d4f',
  locale: 'en-US',
});

Copy to clipboard with callbacks:

Shotmark.start({
  region: el,
  onCopy: (blob) => {
    showToast('Screenshot copied!');
  },
  onDownload: (fileName) => {
    showToast(`Saved as ${fileName}`);
  },
});

08 Full API Reference

Shotmark.start(options)

Launch the screenshot annotation overlay. All options are optional β€” calling with no arguments enters full-page free selection mode.

Region & Behavior

Field Type Default Description
region HTMLElement | Rect β€” Capture area; pass an element or coordinate rect. Omit for free selection
regionPadding number 0 Extra pixels to expand the region
autoAnnotate boolean true Enter annotation mode immediately after capture
trigger HTMLElement β€” Element to temporarily hide during capture

Tools & Appearance

Field Type Default Description
tools GraphType[] all 10 Which tools to show
actions ActionType[] all 4 Action buttons subset
defaultTool GraphType β€” Pre-selected tool
defaultColor string '#FF3B30' Default annotation color
defaultLineWidth number β€” Default line width
theme 'light' | 'dark' 'light' Theme mode
locale 'zh-CN' | 'en-US' 'zh-CN' Language
localeText LocaleTextOverrides β€” Override built-in text
zIndex number 9998 Overlay z-index

Mosaic & Number

Field Type Default Description
mosaicSize number 2 Block size (larger = stronger)
mosaicSoftness number 36 Softness (0~100)
numberStart number 1 Starting number value

Export

Field Type Default Description
fileName string shotmark_timestamp Download filename (no extension)
format 'png' | 'jpeg' 'png' Export format

Callbacks

Field Type Description
onShot (result: ShotmarkResult) => void Capture complete
onShotStart () => void Rasterization started (show loading)
onCancel () => void Cancelled (βœ• or Esc)
onCopy (blob: Blob) => void Copy succeeded
onCopyError (error: unknown) => void Copy failed
onDownload (fileName: string) => void Download succeeded
onDownloadError (error: unknown) => void Download failed
onAnnotationChange (graphs: GraphPath[]) => void Annotation data changed

Shotmark.close()

Programmatically close the overlay and clean up all DOM. Usually unnecessary β€” it auto-closes when the user finishes.

ShotmarkResult (onShot callback parameter)

Field Type Description
image string base64 dataURL
width number Selection width (CSS pixels)
height number Selection height (CSS pixels)
pixWidth number Actual output width (2x retina)
pixHeight number Actual output height (2x retina)

Type note: Rect = { left, top, width, height } (viewport coordinate rectangle)


09 FAQ

Q: Browser support? Chrome 90+, Edge 90+, Firefox 90+, Safari 15+. All modern browsers.

Q: Will it conflict with my UI library? No. Shotmark uses @emotion/css for style isolation β€” class names are unique hashes, no global pollution. Works alongside any framework.

Q: Does screenshot data get uploaded? Never. Zero network requests at runtime. The image only exists in the callback parameter β€” upload is entirely your decision.

Q: React version requirement? React β‰₯ 17.0.0. Both 17 and 18 are supported.

Q: Export quality? 2x pixel density by default. A 300Γ—200 selection outputs a 600Γ—400 image β€” crisp on Retina displays.

Q: Can I use only 3 tools? Absolutely: tools: ['rectangle', 'arrow', 'text'] β€” the toolbar shows just those three.

Q: What happens if I call start() twice? Safe no-op. If the overlay is already open, the second call is ignored.


10 Use Cases

Scenario Who How
πŸ› Bug reporting Testers / Users Screenshot β†’ circle the bug β†’ add text β†’ paste into ticket
πŸ–ΌοΈ Privacy redaction Everyone Phone/ID numbers β†’ mosaic β†’ safe to share
πŸ“ UI review Design / Frontend Measure tool for spacing β†’ annotate diffs β†’ export comparison
πŸ“ Tutorial creation Everyone Step screenshots β†’ numbers + arrows β†’ instant guide
πŸ“Š Data masking Analytics / Ops Report screenshot β†’ mosaic sensitive numbers β†’ safe distribution
🏭 Quality inspection QA / Operations Compare images β†’ annotate issues β†’ upload records

11 More Resources

Development

pnpm storybook        # dev server
pnpm test             # unit tests
pnpm build            # production build
pnpm format           # format code

License

MIT

About

Lightweight React screenshot annotation library. Capture, draw, copy & download in one line. Zero UI dependency, privacy-first (no upload), plugin-based shapes.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages