markinjs
v0.1.0
Published
A lightweight, zero-dependency SVG annotation library for computer vision and image labeling projects
Maintainers
Readme
MarkinJS
A lightweight, zero-dependency SVG annotation library for computer vision and image labeling projects.
Overview
MarkinJS enables developers to create precise, pixel-perfect annotations on images for computer vision applications. Built for simplicity and performance, it provides a complete toolkit for bounding boxes, polygons, and keypoint annotations with advanced features like element binding, containment rules, and hierarchical relationships.
Perfect for:
- Computer vision datasets
- Image labeling workflows
- Data annotation platforms
- ML training data preparation
- Research and prototyping
Demo
Live Demo - See MarkinJS in action with interactive examples
More Examples - Object detection, medical imaging, and advanced use cases
Installation
npm
npm install markinjsCDN
<!-- Latest version -->
<script src="https://unpkg.com/markinjs/dist/markin.min.js"></script>
<!-- Or via jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/markinjs/dist/markin.min.js"></script>ES Module
import MarkinJS from 'markinjs';CommonJS
const MarkinJS = require('markinjs');Script Tag
<script src="dist/markin.min.js"></script>Quick Start
// Create annotator on an existing image
const annotator = MarkinJS.createImageAnnotator('your-image-id', {
zoom: 1.0,
keyboardControls: true
});
// Add a bounding box
annotator.createAnnotation({
class: "person",
bbox: [100, 100, 300, 250]
});
// Listen for events
annotator.on('select', (data) => {
console.log('Selected:', data.element);
});
// Clean up when done
annotator.destroy();Core Features
Annotation Types
- Bounding Boxes - Rectangular regions with resize handles
- Polygons - Precise outlines with draggable vertices
- Keypoints - Point markers for specific features
- Groups - Hierarchical element relationships
Advanced Capabilities
- Element Binding - Child elements move with parents
- Containment Rules - Keep elements within boundaries
- Custom Deletion Rules - Configure cascade deletion behavior
- Keyboard Controls - Precise movement (1px, 10px, 0.2px)
- Event System - Rich interaction events for custom UI
- Zoom Support - Works at any zoom level
- Undo/Redo - Built-in history management
Technical Highlights
- Zero dependencies
- ~49KB minified
- TypeScript declarations included
- ESM, CJS, and IIFE builds
- Modern browser support
- Pixel-perfect precision
- Framework agnostic
API Reference
Initialization
MarkinJS.createImageAnnotator(imageId, options)
Creates an SVG overlay on an existing image element.
const annotator = MarkinJS.createImageAnnotator('my-image', {
zoom: 1.0,
keyboardControls: true,
bindElements: true
});MarkinJS.createAnnotator(svgId, options)
Uses an existing SVG element for annotations.
Core Methods
| Method | Description |
|--------|-------------|
| createAnnotation(options) | Create new annotation |
| getSelectedElement() | Get currently selected element |
| deleteSelectedElement() | Delete selected element |
| enable() / disable() | Toggle annotator state |
| destroy() | Fully destroy the annotator and clean up all resources |
| setZoom(level) | Set zoom level |
| on(event, callback) | Add event listener |
| off(event, callback) | Remove event listener |
| saveState() | Save current state for undo |
| undo() / redo() | Undo/redo operations |
Annotation Options
annotator.createAnnotation({
uuid: "unique-id", // Optional: auto-generated if not provided
class: "person", // Classification label
bbox: [x1, y1, x2, y2], // Bounding box coordinates
segmentation: [x1,y1,x2,y2...], // Polygon points (flat array)
keypoints: [ // Named keypoints
{ name: "head", point: [x, y] },
{ name: "center", point: [x, y] }
],
bindElements: true, // Bind child elements to parent
containRules: ["keypoint"], // Elements to keep inside bbox
deletionRules: { // Custom deletion behavior
"bbox": ["keypoint", "polygon"],
"polygon": ["bbox"]
}
});Events
| Event | Description | Data |
|-------|-------------|------|
| select | Element selected | {element, type, data} |
| deselect | Element deselected | {element, type} |
| annotationcreated | New annotation created | {uuid, class, group, elements} |
| delete | Element deleted | {role, groupId} |
| elementmoved | Element moved | {element, type, deltaX, deltaY} |
| dragstart, drag, dragend | Drag operations | {element, position} |
Examples
Computer Vision Dataset
// Initialize on dataset image
const annotator = MarkinJS.createImageAnnotator('dataset-image');
// Add person detection
annotator.createAnnotation({
class: "person",
bbox: [150, 100, 300, 400],
keypoints: [
{ name: "head", point: [225, 120] },
{ name: "torso", point: [225, 250] }
],
bindElements: true,
containRules: ["keypoint"]
});
// Export annotations
const annotations = annotator.exportAllAnnotations();
console.log(JSON.stringify(annotations));Medical Imaging
const annotator = MarkinJS.createImageAnnotator('medical-scan', {
keyboardControls: true,
deletionRules: {
"bbox": ["keypoint"], // Remove markers with regions
"keypoint": [] // Keep regions when removing markers
}
});
// Add anatomical region
annotator.createAnnotation({
class: "lesion",
bbox: [200, 150, 280, 220],
keypoints: [
{ name: "center", point: [240, 185] },
{ name: "edge", point: [270, 200] }
]
});Interactive Labeling Tool
const annotator = MarkinJS.createImageAnnotator('labeling-canvas');
// Real-time coordinate display
annotator.on('elementmoved', (data) => {
document.getElementById('coordinates').textContent =
`Position: (${Math.round(data.position.x)}, ${Math.round(data.position.y)})`;
});
// Selection info panel
annotator.on('select', (data) => {
document.getElementById('selection-info').innerHTML = `
<h3>Selected: ${data.type}</h3>
<p>Class: ${data.data.class || 'None'}</p>
<p>UUID: ${data.data.uuid || 'None'}</p>
`;
});Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| zoom | Number | 1.0 | Current zoom level |
| keyboardControls | Boolean | true | Enable arrow key movement |
| bindElements | Boolean | true | Bind child to parent elements |
| requireBbox | Boolean | false | Require bbox for all annotations |
| bboxContainPolygon | Boolean | true | Keep polygons inside bbox |
| bboxContainKeypoints | Boolean | true | Keep keypoints inside bbox |
| historyEnabled | Boolean | true | Enable undo/redo history |
| historyMaxStates | Number | 50 | Maximum number of undo states |
| requireSelectionToDrag | Boolean | true | Require selection before dragging |
| deletionRules | Object | See docs | Default deletion behavior |
Browser Support
- All modern browsers (Chrome, Firefox, Safari, Edge)
Contributing
MarkinJS is currently in active development. We welcome feedback and bug reports!
See CONTRIBUTING.md for development setup and contribution guidelines.
Support
- Website: markinjs.com
- Issues: GitHub Issues
- Contact: [email protected]
License
MIT License - see LICENSE file for details.
Maintained by Datamarkin
