#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>;
}