supervision-js
    Preparing search index...

    Presentation Styles

    Python supervision uses annotators such as BoxAnnotator, MaskAnnotator, and LabelAnnotator to decide how detections are drawn.

    In supervision-js, the equivalent concept is split into two public parts:

    • styles resolve how detections should look;
    • annotation renderers select which visualization capabilities contribute to the scene and carry their styles.

    This keeps detections as semantic model output while the renderer owns the performance-sensitive drawing strategy.

    For focused, live examples, open Annotation Renderers. Each built-in renderer has a basketball fixture playground whose controls update both the scene and a minimal setPresentation() snippet.

    Use BaseBoxStyle, BaseMaskStyle, BasePolygonStyle, BasePolylineStyle, BaseKeypointStyle, BaseLabelStyle, BaseInteractionStyle, and BaseFocusStyle for the common path:

    const session = await createMediaSession({
    container,
    media,
    presentation: {
    focusStyle: new BaseFocusStyle(),
    interactionStyle: new BaseInteractionStyle(),
    renderers: [
    annotationRenderers.box({ style: new BaseBoxStyle() }),
    annotationRenderers.mask({
    style: new BaseMaskStyle({ opacity: 0.5 }),
    }),
    annotationRenderers.label({
    style: new BaseLabelStyle({ includeConfidence: true }),
    }),
    ],
    },
    });

    Box shape is just another style option. Use shape and cornerRadius when rounded rectangles are the desired box treatment:

    session.setPresentation({
    renderers: [
    annotationRenderers.box({
    style: new BaseBoxStyle({
    cornerRadius: 8,
    shape: BoxShape.RoundedRect,
    stroke: {
    alignment: BoxStrokeAlignment.Inside,
    color: 0x38bdf8,
    width: 3,
    },
    }),
    }),
    ],
    });

    Style options accept either static values or resolver functions. Use static values for global styling:

    const maskStyle = new BaseMaskStyle({
    color: 0x38bdf8,
    mode: MaskRenderMode.FillAndStroke,
    opacity: 0.65,
    stroke: {
    alpha: 1,
    color: 0xe0f2fe,
    width: 4,
    },
    });

    Use MaskRenderMode.FillOnly or MaskRenderMode.StrokeOnly for fill-only or outline-only masks. Stroke-only masks default to a 1px same-color outline when no explicit stroke is provided.

    Use resolver functions for per-class, per-confidence, or frame-aware styling:

    const boxStyle = new BaseBoxStyle({
    cornerRadius: (detection) => (detection.className === "basketball" ? 999 : 8),
    fill: (detection) => ({
    alpha: 0.15,
    color: detection.className === "person" ? 0x22c55e : 0xa855f7,
    }),
    shape: BoxShape.RoundedRect,
    shouldRender: (detection) => (detection.confidence ?? 0) >= 0.5,
    stroke: (detection) => ({
    alpha: 1,
    color: detection.className === "person" ? 0x22c55e : 0xa855f7,
    width: 3,
    }),
    });

    Keep box variants in options rather than wrapper classes. That makes the style surface easier to compose as more visual knobs arrive.

    Labels resolve from className, metadata.label, or a custom text resolver. Confidence can be included without storing display text on the detection:

    const labelStyle = new BaseLabelStyle({
    background: (detection) => ({
    alpha: 0.78,
    color: detection.className === "basketball" ? 0x7c2d12 : 0x111827,
    }),
    includeConfidence: true,
    textStyle: {
    color: 0xffffff,
    fontSize: 14,
    fontWeight: "700",
    },
    });

    Use offset when labels need to move away from the default top-left box edge:

    const labelStyle = new BaseLabelStyle({
    background: {
    cornerRadius: 6,
    paddingX: 8,
    paddingY: 4,
    },
    includeConfidence: true,
    offset: (detection) => ({
    x: detection.className === "basketball" ? 4 : 0,
    y: 8,
    }),
    placement: LabelPlacement.Bottom,
    });

    Set labels to appear only for the active hover target when persistent labels would be too dense:

    const labelStyle = new BaseLabelStyle({
    includeConfidence: true,
    visibilityMode: LabelVisibilityMode.HoveredOnly,
    });

    Vector geometry uses the same static-or-resolver style model:

    const polygonStyle = new BasePolygonStyle({
    fill: { alpha: 0.18, color: 0x22c55e },
    stroke: { alpha: 1, color: 0x86efac, width: 3 },
    });

    const polylineStyle = new BasePolylineStyle({
    shadowStroke: { alpha: 0.55, color: 0x000000, width: 6 },
    stroke: { alpha: 1, color: 0x38bdf8, width: 4 },
    });

    const keypointStyle = new BaseKeypointStyle({
    edgeShadowStroke: { alpha: 0.65, color: 0x000000, width: 4 },
    edgeStroke: { alpha: 1, color: 0x22c55e, width: 2 },
    markerFill: { alpha: 1, color: 0x22c55e },
    markerStroke: { alpha: 1, color: 0xffffff, width: 2 },
    radius: 6,
    });

    session.setPresentation({
    renderers: [
    annotationRenderers.keypoints({ style: keypointStyle }),
    annotationRenderers.polygon({ style: polygonStyle }),
    annotationRenderers.polyline({ style: polylineStyle }),
    ],
    });

    BaseKeypointStyle draws NotLabeled points as absent, Occluded points as crosses, and Visible points as circles. Pass definitions when class-specific skeleton vertices and edges need their own colors.

    shadowStroke draws a contrast stroke under a polyline or under skeleton edges, so a thin class-colored line still reads where the media beneath it happens to share that color. The default polyline and keypoint styles both carry one.

    Use the shared resolver when boxes, masks, labels, polygons, and keypoints should agree on class color:

    const boxStyle = new BaseBoxStyle({
    stroke: (detection) => ({
    alpha: 1,
    color: resolveDetectionClassColorStyle(detection.className).stroke,
    width: 3,
    }),
    });

    const labelStyle = new BaseLabelStyle({
    background: (detection) => ({
    alpha: 0.85,
    color: resolveDetectionClassColorStyle(detection.className).labelBackground,
    }),
    textStyle: (detection) => ({
    color: resolveDetectionClassColorStyle(detection.className).labelText,
    }),
    });

    Known classes use DEFAULT_DETECTION_CLASS_STYLES. Unknown names are normalized and deterministically assigned from DEFAULT_DETECTION_COLOR_SEQUENCE.

    Presentation can change without rewriting detections:

    session.setPresentation({
    renderers: [
    annotationRenderers.box({ style: boxStyle }),
    annotationRenderers.keypoints({ style: keypointStyle }),
    annotationRenderers.label({ style: labelStyle }),
    annotationRenderers.mask({ style: maskStyle }),
    annotationRenderers.polygon({ style: polygonStyle }),
    annotationRenderers.polyline({ style: polylineStyle }),
    ],
    });

    The renderer list is authoritative: omit a renderer to disable it, and use an empty list to disable every built-in annotation renderer. The direct boxStyle, maskStyle, and related presentation fields remain supported for compatibility and source-specific overrides, but new global presentation code should prefer renderers.

    Global annotation visibility can hide annotations, labels, classes, or specific detection IDs without mutating semantic frames:

    session.setPresentation({
    visibility: {
    hiddenClasses: ["background"],
    hiddenDetectionIds: ["suppressed-1"],
    labelsHidden: false,
    },
    });

    For masks, the renderer may reuse prepared ID-mask artifacts when the new style can be applied through the shader palette. If a style change affects which masks exist or how mask borders are prepared, the renderer rebuilds the affected prepared artifacts in the background.

    Interaction styles draw hover and selected states in a separate overlay layer. They resolve to the same box, mask, and label style contracts as the base presentation. Pointer movement does not rebuild prepared mask artifacts:

    session.setPresentation({
    interactionStyle: new BaseInteractionStyle({
    hovered: {
    maskStyle: new BaseMaskStyle({
    color: 0x38bdf8,
    opacity: 0.18,
    stroke: { alpha: 0.9, color: 0x67e8f9, width: 3 },
    }),
    },
    selected: {
    maskStyle: new BaseMaskStyle({
    color: 0x38bdf8,
    opacity: 0.28,
    stroke: { alpha: 1, color: 0xfde047, width: 5 },
    }),
    },
    }),
    });

    Focus styles dim the rest of the media around the selected or hovered detections. The renderer may use the prepared ID-mask artifact for shape-accurate mask cutouts and falls back to detection rectangles when no mask artifact is available:

    session.setPresentation({
    focusStyle: new BaseFocusStyle({
    fill: {
    alpha: 0.5,
    color: 0x020617,
    },
    targetMode: FocusTargetMode.Selected,
    }),
    });

    Custom styles implement the same resolve(detection, context) contract as the base styles. Return a draw instruction to render the detection, or undefined to skip it.

    const onlyPlayers: BoxStyle = {
    resolve(detection) {
    if (detection.className !== "player" || !detection.rect) {
    return undefined;
    }

    return {
    rect: detection.rect,
    shape: BoxShape.Rect,
    stroke: {
    alpha: 1,
    color: 0xfacc15,
    width: 2,
    },
    };
    },
    };

    Keep style decisions in styles. Keep detection frames focused on model output: geometry, masks, class names, confidence, ids, and metadata.