What is MarkinJS?
MarkinJS is a lightweight, zero-dependency library for creating precise SVG-based annotations on images. It's perfect for computer vision datasets, machine learning training data, and research projects that need pixel-perfect annotation tools.
Installation
Download
Download markin.js
from the releases page and include it in your HTML:
<script src="path/to/markin.js"></script>
Quick Start
Step 1: Prepare Your HTML
<!DOCTYPE html>
<html>
<head>
<title>My Annotation Tool</title>
<script src="markin .js"></script>
</head>
<body>
<img id="my-image" src="image.jpg" alt="Image to annotate">
</body>
</html>
Step 2: Initialize the Annotator
// Create annotator on your image
const annotator = MarkinJS.createImageAnnotator('my-image', {
keyboardControls: true,
zoom: 1.0
});
// Listen for when annotations are created
annotator.on('annotationcreated', function(data) {
console.log('New annotation:', data);
});
Step 3: Create Your First Annotation
// Add a bounding box for object detection
annotator.createAnnotation({
class: "person",
bbox: [100, 100, 300, 400]
});
That's it! You now have a working annotation tool.
Common Use Cases
1. Object Detection Dataset
const detector = MarkinJS.createImageAnnotator('dataset-image');
// Add person detection
detector.createAnnotation({
class: "person",
bbox: [150, 100, 300, 400]
});
// Add car detection
detector.createAnnotation({
class: "car",
bbox: [400, 200, 600, 350]
});
// Export annotations for training
detector.on('annotationcreated', (data) => {
saveAnnotation({
image: 'dataset-image.jpg',
class: data.class,
bbox: getBBoxCoordinates(data.group)
});
});
2. Instance Segmentation
const segmenter = MarkinJS.createImageAnnotator('segmentation-image');
// Create annotation with precise polygon outline
segmenter.createAnnotation({
class: "building",
bbox: [200, 150, 500, 400],
segmentation: [220, 170, 480, 170, 480, 380, 350, 380, 220, 300],
bindElements: true // Polygon moves with bounding box
});
3. Keypoint Detection
const keypointDetector = MarkinJS.createImageAnnotator('keypoint-image');
// Facial keypoint annotation
keypointDetector.createAnnotation({
class: "face",
bbox: [100, 80, 200, 180],
keypoints: [
{ name: "left_eye", point: [130, 110] },
{ name: "right_eye", point: [170, 110] },
{ name: "nose", point: [150, 130] },
{ name: "mouth", point: [150, 150] }
],
containRules: ["keypoint"] // Keep keypoints inside bbox
});
Interactive Features
Keyboard Controls
Once an element is selected, you can move it precisely:
- Arrow Keys: Move 1 pixel
- Shift + Arrow Keys: Move 10 pixels
- Ctrl + Arrow Keys: Move 0.2 pixels (sub-pixel precision)
- Delete Key: Remove selected annotation
Mouse Interactions
- Click: Select elements
- Drag: Move elements around
- Handles: Resize bounding boxes by dragging corners/edges
Event Handling
// Track user interactions
annotator.on('select', (data) => {
console.log('Selected:', data.type, data.class);
showEditPanel(data);
});
annotator.on('elementmoved', (data) => {
console.log('Moved to:', data.position);
updateCoordinateDisplay(data.position);
});
annotator.on('delete', (data) => {
console.log('Deleted annotation:', data.groupId);
updateAnnotationCount();
});
Configuration Options
Basic Configuration
const annotator = MarkinJS.createImageAnnotator('image', {
// Enable keyboard arrow key movement
keyboardControls: true,
// Current zoom level
zoom: 1.0,
// Bind child elements to parent elements
bindElements: true,
// Keep polygons inside bounding boxes
bboxContainPolygon: true,
// Keep keypoints inside bounding boxes
bboxContainKeypoints: true
});
Advanced Deletion Rules
const annotator = MarkinJS.createImageAnnotator('image', {
deletionRules: {
// When bbox is deleted, also delete keypoints and polygons
"bbox": ["keypoint", "polygon"],
// When keypoint is deleted, don't delete anything else
"keypoint": [],
// When polygon is deleted, also delete bbox
"polygon": ["bbox"]
}
});
Best Practices
1. Performance
- Use event delegation for large numbers of annotations
- Clean up event listeners when destroying annotators
- Consider pagination for datasets with many images
2. User Experience
// Provide visual feedback
annotator.on('dragstart', () => {
document.body.style.cursor = 'grabbing';
});
annotator.on('dragend', () => {
document.body.style.cursor = 'default';
});
// Show coordinate information
annotator.on('elementmoved', (data) => {
document.getElementById('coordinates').textContent =
`(${Math.round(data.position.x)}, ${Math.round(data.position.y)})`;
});
3. Data Validation
// Validate annotation data before creating
function createValidatedAnnotation(data) {
if (!data.class) {
throw new Error('Annotation must have a class');
}
if (data.bbox && data.bbox.length !== 4) {
throw new Error('Bbox must have exactly 4 coordinates');
}
annotator.createAnnotation(data);
}
API Reference - Initialization
MarkinJS.createImageAnnotator(imageId, options)
Creates an SVG overlay positioned directly on top of an existing image element.
•
imageId
(String): ID of the image element to overlay•
options
(Object): Configuration options
const annotator = MarkinJS.createImageAnnotator('my-image', {
zoom: 1.0,
keyboardControls: true,
bindElements: true
});
Additional Methods Available:
getImageElement()
: Returns the underlying image elementgetSVGElement()
: Returns the generated SVG elementupdateDimensions()
: Manually updates SVG dimensions to match imagegetContainerElement()
: Returns the container wrapping image and SVG
Core Methods
enable()
Enables the annotator, allowing user interaction.
disable()
Disables the annotator, preventing user interaction.
setZoom(zoomLevel)
Sets the zoom level for the annotator.
•
zoomLevel
(Number): Zoom level (e.g., 1.0 = 100%, 2.0 = 200%)
getSelectedElement()
Returns information about the currently selected element.
•
element
(SVGElement): The DOM element•
type
(String): Element type ("rect", "circle", "polygon", "g")•
data
(Object): Element-specific data•
uuid
(String): Element UUID (if available)•
class
(String): Element class (if available)
deleteSelectedElement()
Deletes the currently selected element and applies deletion rules.
Event System
on(eventName, callback)
Registers an event handler.
•
eventName
(String): Name of the event•
callback
(Function): Handler function
off(eventName, callback)
Removes an event handler.
Available Events
Event | Description | Data Object |
---|---|---|
select |
Element selected | {element, type, data, uuid, class} |
deselect |
Element deselected | {element, type} |
annotationcreated |
New annotation created | {uuid, class, group, elements} |
elementmoved |
Element moved | {element, type, deltaX, deltaY, position} |
delete |
After element deletion | {role, groupId} |
Annotation Creation
createAnnotation(options)
Creates a new annotation with the specified options.
Annotation Options:
{
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"]
}
}
Element Management
Keyboard Controls
When enabled, selected elements can be moved using:
- Arrow keys: Move by 1px
- Shift + Arrow keys: Move by 10px
- Ctrl + Arrow keys: Move by 0.2px (fine adjustment)
- Delete key: Delete selected element
enableKeyboardControls()
Enables keyboard controls for element movement and deletion.
disableKeyboardControls()
Disables keyboard controls.
setDeletionRules(rules)
Sets custom deletion rules for the annotator.
annotator.setDeletionRules({
"bbox": ["keypoint", "polygon", "group"],
"keypoint": [],
"polygon": ["bbox"]
});
Advanced Usage Examples
Custom Event Handling
const annotator = MarkinJS.createImageAnnotator('image');
// Track all annotation changes
annotator.on('annotationcreated', (data) => {
console.log(`Created ${data.class} annotation:`, data.uuid);
});
annotator.on('delete', (data) => {
console.log(`Deleted annotation group:`, data.groupId);
});
// Real-time coordinate tracking
annotator.on('elementmoved', (data) => {
updateCoordinateDisplay(data.element, data.position);
});
Complex Annotation Creation
// Medical imaging annotation
const medicalAnnotation = annotator.createAnnotation({
class: "lesion",
uuid: "lesion_001",
bbox: [200, 150, 350, 280],
keypoints: [
{ name: "center", point: [275, 215] },
{ name: "boundary_north", point: [275, 160] },
{ name: "boundary_south", point: [275, 270] }
],
segmentation: [210, 160, 340, 160, 340, 270, 210, 270],
bindElements: true,
containRules: ["keypoint", "polygon"],
deletionRules: {
"bbox": ["keypoint", "polygon"],
"keypoint": [],
"polygon": ["keypoint"]
}
});
Ready to Build?
Explore examples or start building your annotation tool