#API reference

#Component

#<MuscleMap />

Exported from @gravn/muscle-highlighter/native and @gravn/muscle-highlighter/web. Identical props on both.

Prop Type Default
sex "male" | "female" "male" Which body to draw.
view "front" | "back" "front" Which face of the body.
intensities MuscleIntensityMap {} Sparse muscle code → level, or { left, right }.
colors IntensityAnchors green/amber/red Sparse level → hex anchors.
baseColor string "#3f4654" Fill for muscles with no intensity.
bodyFill string "#2a2f3a" Silhouette fill beneath the muscles. "none" for transparent.
outlineColor string "none" Silhouette stroke, drawn over the muscles.
outlineWidth number 2 Silhouette stroke width.
muscleStroke string - Optional stroke around each muscle region.
muscleStrokeWidth number 1 Per-muscle stroke width.
width number 200 Rendered width.
height number 400 Rendered height.
scale number 1 Multiplier over the default size. Ignored when width/height are given.
onMusclePress (event: MusclePressEvent) => void - Tap or click handler.
style unknown - Passed through to the root SVG element.
accessibilityLabel string "<sex> body, <view> view" Overrides the generated label.
interface MusclePressEvent {
  region: MuscleCode; // the drawn region tapped - not the aliased code
  side: "left" | "right" | "common";
  level: number | null; // null when the region was not highlighted
}

#Catalogue

#MUSCLE_METADATA

Record<MuscleCode, MuscleDefinition> - the full catalogue.

interface MuscleDefinition {
  code: MuscleCode;
  name: string;
  status: "supported" | "extended";
  resolution: "drawn" | "splittable" | "deep" | "side-view";
  aliasOf?: MuscleCode; // absent for drawn codes
  aliasNote?: string; // why it is aliased; present whenever aliasOf is
}

#ALL_MUSCLE_CODES

MuscleCode[] - every code, as a plain array.

#isMuscleCode(candidate: string): candidate is MuscleCode

Type guard. Use it at the boundary where backend data enters the app - an unrecognised code would otherwise be dropped from the map without a word.

#resolveToDrawnRegion(code: MuscleCode): MuscleCode

Resolves a code onto the region that actually renders it. Drawn codes resolve to themselves.

#listCodesRenderedBy(regionCode: MuscleCode): MuscleCode[]

The reverse lookup: the region's own code, followed by every code aliased onto it. Useful for tooltips - tapping the upper back can say it covers lats, teres and infraspinatus too.

#Geometry

#getBodyMap(sex, view): BodyMap

One of the four diagrams.

interface BodyMap {
  sex: Sex;
  view: BodyView;
  viewBox: string; // differs per body and view - carried with the data, never assumed
  outline: string; // the silhouette path
  regions: Partial<Record<MuscleCode, MuscleRegionGeometry>>;
}

interface MuscleRegionGeometry {
  left?: string[];
  right?: string[];
  common?: string[]; // geometry not split down the midline
}

A side key is present only when that side carries geometry, so a missing left means the region genuinely has no left half.

#BODY_MAPS

Record<Sex, Record<BodyView, BodyMap>> - all four, if you would rather index than call.

#getVisibleViews(code, sex): BodyView[]

Which views a code renders on for a given body. Extended codes report the views of the region that renders them.

#isVisibleOn(code, sex, view): boolean

Whether a code resolves to a region present on that specific diagram.

#listCodesMissingFrom(sex, view): MuscleCode[]

Every code with no geometry on that diagram.

#Resolution

#resolveIntensities(intensityMap, bodyMap): ResolvedIntensities

The single place aliasing, per-side distribution and collision merging happen. Both renderers and any headless consumer share it, so behaviour cannot diverge.

interface ResolvedIntensities {
  regions: Partial<Record<MuscleCode, Partial<Record<BodySide, number>>>>;
  unrenderedCodes: MuscleCode[];
}

Three steps, in order: alias each code onto its drawn region; spread the intensity across the sides that region has; merge collisions by taking the maximum.

#Colour

#createIntensityScale(anchors?): IntensityScale

Builds a (level: number) => string from a sparse anchor map. Defaults to green/amber/red. Parses anchors once at construction, so a malformed colour throws immediately rather than mid-render, and memoises per level.

#buildIntensityRamp(anchors?, min?, max?): Array<{ level, color }>

Every integer step, for legends and swatch strips. Defaults to levels 1 through 10.

#normalizeToIntensity(measure, maximumMeasure, min?, max?): number | null

Maps a raw measure onto the scale. Returns null for a measure of zero or below, meaning "not worked" - which renders as the base colour rather than as intensity 1.

#Colour conversion

hexToOklch(hex): OklchColor, oklchToHex(color): string, and mixOklch(from, to, position): OklchColor. Dependency-free. Accepts #rgb, #rgba, #rrggbb and #rrggbbaa; alpha is preserved and dropped again when fully opaque.

interface OklchColor {
  lightness: number; // ~0 (black) to 1 (white)
  chroma: number; // 0 (grey) upwards; sRGB colours sit below ~0.37
  hue: number; // degrees, 0-360
  alpha: number; // 0-1
}

#Types

MuscleCode, MuscleDefinition, MuscleStatus, MuscleResolution, Sex, BodyView, BodySide, BodyMap, MuscleRegionGeometry, MuscleIntensity, MuscleIntensityMap, ResolvedIntensities, ResolvedRegionIntensity, IntensityAnchors, IntensityScale, OklchColor, MuscleMapProps, MusclePressEvent, SvgPrimitives.

#SvgPrimitives

The contract that lets one renderer serve both platforms. You only need this if you are binding a third renderer - a canvas backend, say, or a test double.

interface SvgPrimitives {
  Svg: ComponentType<any>;
  G: ComponentType<any>;
  Path: ComponentType<any>;
  pressProps: (onPress?: () => void) => Record<string, unknown>;
  accessibilityProps: (label: string) => Record<string, unknown>;
}