This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/web/headless/ink.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Headless ink annotations | Nutrient Web SDK

The instance.annotations.ink namespace exposes the live drawing state the SDK tracks while the user is drawing an ink annotation. Combine it with the existing ink configuration and event surfaces to build an ink experience that doesn’t go through the default toolbar.

Looking for the visual side of this? See the supported slots reference for replacing the inline ink toolbar that appears when ink mode is active. This page covers the programmatic surface you’ll call from inside that custom UI.

When to use this

Reach for the headless ink API when you’re building:

  • A floating brush toolbar that lives outside the viewer and stays visible across documents.
  • A status indicator that reflects whether the user is currently in the middle of a stroke.
  • An analytics or collaboration layer that tracks how long users spend drawing.
  • Switching the eraser between point and stroke modes, either at load time or at runtime from a custom toolbar.

API reference

The headless ink surface splits into two parts: the live drawing state you can read while a stroke is in progress, and the eraser mode configuration.

Drawing state

SymbolNotes
instance.annotations.ink.getDrawingState()Returns a snapshot of the current ink drawing state. Reads safely at any time; useful when polling from a render loop.

getDrawingState() returns an object with:

  • isDrawing: boolean — Whether the user is currently in the middle of a stroke.
  • activeAnnotationId: string | null — The ID of the ink annotation being drawn, or null when idle.
  • lastDrawStartAt: number | null — Timestamp (ms since epoch) when the most recent stroke started.
  • lastDrawEndAt: number | null — Timestamp when the most recent stroke ended.
  • lastDrawDurationMs: number | null — Duration of the most recent stroke in milliseconds.

The state is transient telemetry; it doesn’t include the stroke geometry, color, or width. For brush settings, refer to the inkAnnotation annotation preset. For the rendered stroke geometry, read the annotation directly once it has been created.

Eraser configuration

SymbolNotes
NutrientViewer.InkEraserModeEnum with two values: POINT (erase individual points) and STROKE (erase whole strokes).
Configuration.inkEraserModeInitial eraser mode, passed to NutrientViewer.load(). Defaults to InkEraserMode.POINT.
instance.annotations.ink.setEraserMode(mode)Updates the eraser mode at runtime. Accepts an InkEraserMode value and returns the applied mode. Use this from a custom toolbar after load().

Events

EventWhen it fires
ink.drawingEmitted during an active ink stroke with payload { annotation: InkAnnotation }, where annotation is the in-progress stroke. Pair with getDrawingState() for lifecycle telemetry.

Example: A status indicator wired to drawing state

This example renders an indicator outside the viewer container that flips when the user starts or stops drawing:

const instance = await NutrientViewer.load({
container: "#viewer",
document: "contract.pdf"
});
const indicator = document.getElementById("drawing-indicator");
function render() {
const state = instance.annotations.ink.getDrawingState();
indicator.classList.toggle("is-active", state.isDrawing);
indicator.textContent = state.isDrawing
? "Drawing…"
: state.lastDrawDurationMs != null
? `Last stroke: ${Math.round(state.lastDrawDurationMs)}ms`
: "Idle";
}
instance.addEventListener("ink.drawing", render);
render();

You can also poll getDrawingState() synchronously from a render loop — for example, when you want to step the indicator at a fixed frame rate:

function frame() {
const state = instance.annotations.ink.getDrawingState();
indicator.classList.toggle("is-active", state.isDrawing);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);

Example: Configure the eraser

Set the initial eraser mode by passing inkEraserMode to NutrientViewer.load():

const instance = await NutrientViewer.load({
container: "#viewer",
document: "contract.pdf",
inkEraserMode: NutrientViewer.InkEraserMode.STROKE
});

InkEraserMode.POINT (the default) erases individual points as the eraser passes over them. InkEraserMode.STROKE erases the entire stroke under the eraser as a single action.

Switch the mode at runtime by calling setEraserMode(), which is typically wired up to a custom toolbar control:

document.getElementById("eraser-mode-stroke").onclick = () => {
instance.annotations.ink.setEraserMode(NutrientViewer.InkEraserMode.STROKE);
};
document.getElementById("eraser-mode-point").onclick = () => {
instance.annotations.ink.setEraserMode(NutrientViewer.InkEraserMode.POINT);
};