{"id":20610897,"url":"https://github.com/darkroomengineering/elastica","last_synced_at":"2026-03-12T14:39:51.486Z","repository":{"id":251065781,"uuid":"835259375","full_name":"darkroomengineering/elastica","owner":"darkroomengineering","description":"Physics engine for 2D rigid body collisions with React bindings.","archived":false,"fork":false,"pushed_at":"2026-01-30T18:42:25.000Z","size":137621,"stargazers_count":38,"open_issues_count":1,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-02-08T06:13:17.593Z","etag":null,"topics":["aabb","collision","javascript-library","react"],"latest_commit_sha":null,"homepage":"https://elastica.darkroom.engineering","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/darkroomengineering.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-07-29T13:25:26.000Z","updated_at":"2026-01-30T18:50:53.000Z","dependencies_parsed_at":"2024-07-31T18:18:15.800Z","dependency_job_id":"a9112704-8126-4d0d-88f6-3d3450f67a55","html_url":"https://github.com/darkroomengineering/elastica","commit_stats":null,"previous_names":["darkroomengineering/elastica"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/darkroomengineering/elastica","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkroomengineering%2Felastica","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkroomengineering%2Felastica/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkroomengineering%2Felastica/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkroomengineering%2Felastica/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darkroomengineering","download_url":"https://codeload.github.com/darkroomengineering/elastica/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darkroomengineering%2Felastica/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30428518,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T14:34:45.044Z","status":"ssl_error","status_checked_at":"2026-03-12T14:09:33.793Z","response_time":114,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aabb","collision","javascript-library","react"],"created_at":"2024-11-16T10:18:17.850Z","updated_at":"2026-03-12T14:39:51.481Z","avatar_url":"https://github.com/darkroomengineering.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# Elastica\n\nPhysics engine for 2D rigid body collisions with React bindings.\n\n[Demo](https://elastica.darkroom.engineering/)\n\n## Features\n\n- **Two collision modes**: AABB (axis-aligned) and OBB (oriented/rotational)\n- **Two rendering modes**: DOM (CSS transforms) and Canvas (2D batched rendering)\n- **Shape support**: Rectangles and circles\n- **Frame-rate independent**: Fixed timestep physics with accumulator\n- **Spatial hashing**: Efficient broad-phase collision detection\n- **Configurable borders**: Rigid (bounce) or periodic (wrap-around)\n\n## Installation\n\n```bash\nnpm i @darkroom.engineering/elastica\n```\n\n## Quick Start\n\n### DOM Mode\n\n```tsx\nimport {\n  DomElastica,\n  BoundaryBox,\n  initalConditionsPresets,\n  updatePresets,\n} from '@darkroom.engineering/elastica/react'\n\nfunction App() {\n  return (\n    \u003cDomElastica\n      config={{\n        collisions: true,\n        borders: 'rigid',\n      }}\n      initialCondition={initalConditionsPresets.random}\n      update={updatePresets.dvdScreenSaver}\n    \u003e\n      {items.map((item, i) =\u003e (\n        \u003cBoundaryBox key={i}\u003e\n          \u003cdiv className=\"box\"\u003e{item.name}\u003c/div\u003e\n        \u003c/BoundaryBox\u003e\n      ))}\n    \u003c/DomElastica\u003e\n  )\n}\n```\n\n### Canvas Mode\n\n```tsx\nimport {\n  CanvasElastica,\n  CanvasBox,\n  initalConditionsPresets,\n  updatePresets,\n} from '@darkroom.engineering/elastica/react'\n\nfunction App() {\n  return (\n    \u003cCanvasElastica\n      config={{\n        collisions: true,\n        borders: 'rigid',\n      }}\n      initialCondition={initalConditionsPresets.random}\n      update={updatePresets.dvdScreenSaver}\n    \u003e\n      {particles.map((p, i) =\u003e (\n        \u003cCanvasBox\n          key={i}\n          width={p.width}\n          height={p.height}\n          fill={p.color}\n        /\u003e\n      ))}\n    \u003c/CanvasElastica\u003e\n  )\n}\n```\n\n## API\n\n### DomElastica / CanvasElastica\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `config` | `object` | See below | Physics configuration |\n| `initialCondition` | `function` | `() =\u003e {}` | Called once on mount to set initial positions/velocities |\n| `update` | `function` | `() =\u003e {}` | Called each physics step to apply forces |\n| `showHashGrid` | `boolean` | `false` | Visualize spatial hash grid (debug) |\n\n### Config Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `collisions` | `boolean` | `true` | Enable collision detection |\n| `borders` | `'rigid'` \\| `'periodic'` \\| `false` | `'rigid'` | Border behavior |\n| `gridSize` | `number` | `8` | Spatial hash cell size (should be \u003e= largest element) |\n| `useOBB` | `boolean` | `false` | Enable rotational physics (OBB mode) |\n| `containerOffsets` | `object` | `{ top: 0, bottom: 0, left: 0, right: 0 }` | Inset simulation bounds |\n| `defaultMass` | `number` | `1` | Default mass for elements |\n| `defaultRestitution` | `number` | `0.8` | Default bounciness (0-1) |\n| `solver.fixedDeltaTime` | `number` | `16.67` | Physics timestep in ms |\n| `solver.substeps` | `number` | `1` | Physics substeps per frame |\n\n### BoundaryBox (DOM)\n\nWraps DOM elements for physics simulation.\n\n```tsx\n\u003cBoundaryBox\n  isStatic={false}      // Static elements don't move but can be collided with\n  mass={1}              // Element mass (OBB mode)\n  restitution={0.8}     // Bounciness (OBB mode)\n  displayScale={1}      // Visual scale (doesn't affect collision bounds)\n  shape=\"rectangle\"     // 'rectangle' or 'circle'\n\u003e\n  \u003cdiv\u003eContent\u003c/div\u003e\n\u003c/BoundaryBox\u003e\n```\n\n### CanvasBox (Canvas)\n\nDefines particle data for canvas rendering.\n\n```tsx\n\u003cCanvasBox\n  width={50}\n  height={50}\n  fill=\"#ff0000\"\n  stroke=\"#000000\"\n  strokeWidth={2}\n  shape=\"rectangle\"     // 'rectangle' or 'circle'\n  isStatic={false}\n  mass={1}\n  restitution={0.8}\n/\u003e\n```\n\n### Hooks\n\n```tsx\n// Access engine instance and context\nconst { elastica, addBox, removeBox } = useDomElastica()\nconst { elastica, registerParticle, unregisterParticle } = useCanvasElastica()\n\n// Control playback\nconst ref = useRef\u003cDomElasticaRef\u003e(null)\nref.current?.pause()\nref.current?.play()\n```\n\n### Update Callback\n\nThe `update` function receives physics state each frame:\n\n```tsx\nfunction customUpdate({\n  boxes,           // Element data array\n  positions,       // [x, y] positions (center)\n  velocities,      // [vx, vy] velocities\n  externalForces,  // Apply forces here\n  deltaTime,       // Fixed timestep\n  angles,          // Rotation angles (OBB mode)\n  angularVelocities,\n  masses,\n  bounced,         // Collision flags\n  isStatic,\n}: UpdateParams) {\n  boxes.forEach((_, index) =\u003e {\n    if (isStatic[index]) return\n\n    const velocity = velocities[index]\n    const position = positions[index]\n\n    // Apply gravity\n    velocity[1] += 0.001 * deltaTime\n\n    // Integrate position\n    position[0] += velocity[0] * deltaTime\n    position[1] += velocity[1] * deltaTime\n  })\n}\n```\n\n## Presets\n\n```tsx\nimport {\n  initalConditionsPresets,\n  updatePresets,\n} from '@darkroom.engineering/elastica/react'\n\n// Initial conditions\ninitalConditionsPresets.random      // Random positions and velocities\ninitalConditionsPresets.randomOBB   // Random with rotation (OBB mode)\n\n// Update functions\nupdatePresets.dvdScreenSaver        // Constant velocity, bounce on edges\nupdatePresets.dvdScreenSaverOBB     // With rotation\nupdatePresets.DragAndGravity        // Gravity + drag interaction\nupdatePresets.rightFlow             // Horizontal flow field\n```\n\n## Engine Only\n\nUse the physics engine without React:\n\n```ts\nimport Elastica from '@darkroom.engineering/elastica'\n\nconst engine = new Elastica({\n  collisions: true,\n  borders: 'rigid',\n  useOBB: true,\n})\n\nengine.setContainer({ width: 800, height: 600 })\n\n// Physics loop\nfunction tick() {\n  engine.update(elements, (instance) =\u003e {\n    // Apply forces, integrate positions\n  })\n  requestAnimationFrame(tick)\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarkroomengineering%2Felastica","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarkroomengineering%2Felastica","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarkroomengineering%2Felastica/lists"}