Skip to content

JScafidi616/Dynamic-Body-muscle

Repository files navigation

πŸ‹οΈ Dynamic Body Muscle

An interactive, lightweight React component library for visualizing and selecting body muscles. Perfect for fitness apps, workout planners, physiotherapy tools, and anatomy education.

Demo_click Demo_programmatic

TypeScript React License

✨ Features

  • 🎯 Dual Interaction Modes: Click-based selection or programmatic control
  • πŸ”„ Smart Muscle Pairing: Automatically sync left/right muscle groups
  • 🎨 Customizable Styling: Override colors, opacity, and visual states
  • πŸ“± Responsive: Works on desktop and mobile
  • πŸͺΆ Lightweight: Zero dependencies (just React)
  • πŸ”’ Type-Safe: Full TypeScript support
  • 🌳 Tree-Shakeable: Import only what you need
  • β™Ώ Accessible: Proper hover states and keyboard support


πŸ“¦ Installation

npm install dynamic-body-muscle-map
# or
yarn add dynamic-body-muscle-map
# or
pnpm add dynamic-body-muscle-map

πŸš€ Quick Start

import { BodyMap, useMuscleState } from 'dynamic-body-muscle-map';

function App() {
	const { selected, toggleSelect } = useMuscleState();

	return (
		<BodyMap
			selected={selected}
			interactive={true}
			onMuscleClick={toggleSelect}
		/>
	);
}

πŸ“– API Reference

<BodyMap>

The main component for rendering the interactive body muscle map.

Props

Prop Type Default Description
highlighted MuscleId[] [] Array of muscle IDs to highlight
selected MuscleId[] [] Array of muscle IDs to mark as selected
interactive boolean | MuscleId[] true Enable click interaction (true/false or array of specific muscles)
onMuscleClick (id: MuscleId) => void - Callback when a muscle is clicked
onMuscleHover (id: MuscleId | null) => void - Callback when a muscle is hovered
showHitLayers boolean false Show invisible hit detection layers (debug)
styles MuscleStyles Default styles Custom styles for different muscle states
className string '' Additional CSS class for the SVG container

Example

<BodyMap
	highlighted={['RightBicep', 'LeftBicep']}
	selected={['RightQuad']}
	interactive={['RightBicep', 'LeftBicep', 'Chest']}
	onMuscleClick={(id) => console.log('Clicked:', id)}
	onMuscleHover={(id) => console.log('Hovered:', id)}
	showHitLayers={false}
	styles={{
		highlighted: { fill: '#ff6b6b', opacity: 0.8 },
		selected: { fill: '#4ecdc4', opacity: 0.9 },
	}}
/>

useMuscleState()

A React hook for managing muscle selection state.

Returns

Property Type Description
highlighted MuscleId[] Currently highlighted muscles
selected MuscleId[] Currently selected muscles
toggleHighlight (id: MuscleId) => void Toggle highlight for a single muscle
highlightMuscles (ids: MuscleId[]) => void Set highlighted muscles (replaces existing)
clearHighlights () => void Clear all highlights
toggleSelect (id: MuscleId) => void Toggle selection for a single muscle
selectMuscles (ids: MuscleId[]) => void Set selected muscles (replaces existing)
clearSelections () => void Clear all selections
reset () => void Clear both highlights and selections

Example

const {
  selected,
  toggleSelect,
  selectMuscles,
  clearSelections,
} = useMuscleState();

// Toggle individual muscle
<button onClick={() => toggleSelect('RightBicep')}>
  Toggle Right Bicep
</button>

// Select multiple muscles
<button onClick={() => selectMuscles(['RightBicep', 'LeftBicep'])}>
  Select Both Biceps
</button>

// Clear all
<button onClick={clearSelections}>Clear All</button>

🎨 Custom Styling

Override default styles for different muscle states:

const customStyles = {
	default: {
		fill: '#c2c2c2',
		opacity: 1,
	},
	highlighted: {
		fill: '#ff0000',
		opacity: 0.8,
		stroke: '#000000',
		strokeWidth: 2,
	},
	selected: {
		fill: '#00ff00',
		opacity: 0.9,
		stroke: '#ffffff',
		strokeWidth: 3,
	},
	hovered: {
		fill: '#0000ff',
		opacity: 0.85,
	},
	disabled: {
		fill: '#999999',
		opacity: 0.5,
	},
};

<BodyMap styles={customStyles} />;

πŸ”§ Utility Functions

getMuscleById(id: string)

Get muscle data by ID.

import { getMuscleById } from 'dynamic-body-muscle-map';

const muscle = getMuscleById('RightBicep');
console.log(muscle.name); // "Right Bicep"
console.log(muscle.group); // "RightArm"

getMusclesByGroup(groupName: string)

Get all muscles in a group.

import { getMusclesByGroup } from 'dynamic-body-muscle-map';

const armMuscles = getMusclesByGroup('RightArm');
console.log(armMuscles); // [{ id: 'RightBicep', ... }, ...]

getPairedMuscle(id: string)

Get the paired muscle (left ↔ right).

import { getPairedMuscle } from 'dynamic-body-muscle-map';

const paired = getPairedMuscle('RightBicep');
console.log(paired); // "LeftBicep"

πŸ’‘ Usage Examples

Click Mode (Interactive)

import { BodyMap, useMuscleState } from 'dynamic-body-muscle-map';

function InteractiveExample() {
	const { selected, toggleSelect, clearSelections } = useMuscleState();

	return (
		<div>
			<BodyMap
				selected={selected}
				interactive={true}
				onMuscleClick={toggleSelect}
			/>
			<button onClick={clearSelections}>Clear Selection</button>
			<p>Selected: {selected.join(', ')}</p>
		</div>
	);
}

Programmatic Mode (Controlled)

import { BodyMap } from 'dynamic-body-muscle-map';
import { useState } from 'react';

function ProgrammaticExample() {
	const [highlighted, setHighlighted] = useState(['RightBicep', 'LeftBicep']);

	return (
		<div>
			<BodyMap highlighted={highlighted} interactive={false} />
			<button onClick={() => setHighlighted(['Chest', 'AbsUpper'])}>
				Highlight Chest & Abs
			</button>
		</div>
	);
}

Mixed Mode (Partial Interaction)

import { BodyMap, useMuscleState } from 'dynamic-body-muscle-map';

function MixedExample() {
	const { selected, toggleSelect } = useMuscleState();
	const [highlighted] = useState(['Chest', 'AbsUpper']);

	return (
		<BodyMap
			highlighted={highlighted}
			selected={selected}
			interactive={['RightBicep', 'LeftBicep']} // Only biceps clickable
			onMuscleClick={toggleSelect}
		/>
	);
}

Muscle Pairing

import {
	BodyMap,
	useMuscleState,
	getPairedMuscle,
} from 'dynamic-body-muscle-map';

function PairingExample() {
	const { selected, toggleSelect } = useMuscleState();
	const [syncPairs, setSyncPairs] = useState(true);

	const handleMuscleClick = (muscleId) => {
		toggleSelect(muscleId);

		if (syncPairs) {
			const paired = getPairedMuscle(muscleId);
			if (paired) toggleSelect(paired);
		}
	};

	return (
		<div>
			<label>
				<input
					type='checkbox'
					checked={syncPairs}
					onChange={(e) => setSyncPairs(e.target.checked)}
				/>
				Sync Muscle Pairs
			</label>

			<BodyMap
				selected={selected}
				interactive={true}
				onMuscleClick={handleMuscleClick}
			/>
		</div>
	);
}

πŸ—οΈ Architecture

Component Structure

graph TD
    A[BodyMap] --> B[MuscleGroup]
    B --> C[Muscle]
    C --> D[Visible Path]
    C --> E[Hit Layer Path]

    A --> F[useMuscleState Hook]
    F --> G[State Management]

    A --> H[MUSCLE_GROUPS Data]
    H --> I[muscleData.ts]

    style A fill:#4ecdc4
    style F fill:#ff6b6b
    style H fill:#1E5474FF
Loading

Data Flow

sequenceDiagram
    participant User
    participant BodyMap
    participant Muscle
    participant State

    User->>BodyMap: Click muscle
    BodyMap->>Muscle: Detect click
    Muscle->>BodyMap: Emit onMuscleClick(id)
    BodyMap->>State: toggleSelect(id)
    State->>BodyMap: Update selected[]
    BodyMap->>Muscle: Re-render with new state
    Muscle->>User: Visual feedback
Loading

State Management

stateDiagram-v2
    [*] --> Idle
    Idle --> Highlighted: highlightMuscles()
    Idle --> Selected: toggleSelect()
    Highlighted --> Selected: toggleSelect()
    Selected --> Highlighted: clearSelections()
    Highlighted --> Idle: clearHighlights()
    Selected --> Idle: clearSelections()
    Idle --> Hovered: Mouse Enter
    Hovered --> Idle: Mouse Leave
Loading

πŸ—‚οΈ Available Muscle Groups

Arms

  • Biceps
  • Forearms
  • Shoulders

Torso

  • Chest
  • AbsUpper
  • AbsLower
  • AbsObliques

Legs

  • Quads
  • Calfs
  • Adductors

πŸ› οΈ Development

Adding Custom SVG

  1. Place your SVG in src/assets/body-map-[gender]_[view].svg
  2. Run the parser: npx tsx scripts/parse-svg.ts
  3. Generated files:
    • src/types/muscles.ts - TypeScript types
    • src/utils/muscleData.ts - Muscle data & pairs

SVG Requirements

  • Each muscle should be a <path> element with a unique id
  • Optional: Add [MuscleID]_Hit paths for better click detection
  • Group muscles using <g id="[GroupName]Group">

Example:

<g id="RightArmGroup">
  <path id="RightBicep" d="M..." fill="#c2c2c2" />
  <path id="RightBicep_Hit" d="M..." fill="transparent" opacity="0" />
</g>

πŸ“Š Bundle Size

Package Size (gzipped)
Core Component ~8 KB
Full Library ~15 KB

πŸ—ΊοΈ Roadmap

  • Back view support
  • Male/Female body variations
  • Animation transitions
  • Preset workout configurations
  • Export/Import muscle selections
  • Touch gestures for mobile
  • Accessibility improvements (ARIA labels)

🀝 Contributing

Contributions are welcome! Please read our Contributing Guide for details. //Still pending at the moment...

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

πŸ“„ License

MIT Β© Joseph Scafidi


πŸ™ Acknowledgments

  • SVG assets based on anatomical references
  • Built with React, TypeScript, and Vite
  • Inspired by fitness and physiotherapy applications

πŸ“¬ Support


Made with ❀️ for the fitness and health community

About

Just a library to add your own interactive body muscle svg to your project!

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages