# Wide Contacts vs Manifold Constraints in Box3D's Narrow-Phase

> Understand wide contacts vs manifold constraints in Box3D's narrow-phase. Discover how SIMD batch processing and precise geometric data optimize collision detection.

- Repository: [Erin Catto/box3d](https://github.com/erincatto/box3d)
- Tags: deep-dive
- Published: 2026-08-01

---

**Wide contacts batch process collision constraints using SIMD vector lanes for parallel solver execution, while manifold constraints store the precise geometric contact data—normals, penetration depths, and up to four contact points—generated by narrow-phase collision detection.**

The erincatto/box3d physics engine employs a dual-representation strategy to balance geometric fidelity with computational throughput. Understanding the distinction between **wide contacts and manifold constraints** is critical for optimizing collision response, as these structures serve distinct roles in converting raw intersection data into solver-ready constraints.

## Manifold Constraints: Geometric Collision Data

Manifold constraints represent the raw output of the narrow-phase collision detection system. When two convex shapes intersect, the engine computes a detailed geometric description of the contact region required for accurate impulse calculation and friction modeling.

### The b3Manifold Structure

Defined in **src/manifold.h**, the `b3Manifold` structure stores contact geometry in scalar form:

```c
typedef struct b3Manifold {
    b3Vec3 normal;
    float depth;
    int pointCount;
    b3Vec3 points[4];
} b3Manifold;

```

This structure captures the contact normal, penetration depth, and up to four contact points. Each manifold persists in the constraint graph (`b3ContactConstraintManifold`) across simulation steps, enabling temporal coherence and warm-starting for the solver.

### Narrow-Phase Generation

The narrow-phase populates these structures during shape intersection tests. In **src/manifold.c**, collision functions such as `b3ComputeManifoldConvexConvex` clip incident faces against reference faces to fill the manifold:

```c
b3Manifold manifold;
b3ComputeManifoldConvexConvex(shapeA, shapeB, &manifold);

```

Manifolds preserve per-point data in scalar arrays because geometric algorithms require sequential, branching logic that does not vectorize efficiently.

## Wide Contacts: SIMD Solver Optimization

Wide contacts transform scalar manifold data into a vectorized format optimized for the constraint solver. This representation allows the engine to process multiple contacts simultaneously using single-instruction-multiple-data (SIMD) operations.

### The b3ContactConstraintWide Architecture

Declared in **src/solver.h** (lines 92-222), the `b3ContactConstraintWide` structure packs scalar contact values into vector types—typically 4-wide floats—enabling the solver to process four constraints in parallel:

```c
// Block for iterating across wide contacts. For prepare and store.
b3_wideContactBlock,
...
// Flat view of the wide contact constraint array used by prepare and store.
struct b3ContactConstraintWide* wideConstraints;
b3WidePrepareSpan* widePrepareSpans;
int wideContactCount;

```

### Stack Allocation and Block Processing

During the solver step in **src/solver.c**, wide constraints are allocated as a contiguous array on the physics world's stack:

```c
b3ContactConstraintWide* wideConstraints =
    (b3ContactConstraintWide*)b3StackAlloc(&world->stack,
        wideContactCount * b3GetWideContactConstraintByteCount(),
        "wide contacts");
...
stepContext->wideConstraints = wideConstraints;

```

The solver treats this array as a flat parallel-for workload using `b3InitBlocks` and `b3_wideContactBlock`, maximizing cache locality and minimizing branch divergence. Unlike manifolds, wide contacts are temporary structures cleared after each simulation step.

## From Geometry to Execution: The Conversion Pipeline

The transition from geometric manifolds to solver constraints occurs during the prepare phase. The engine extracts data from `b3Manifold` instances and widens the values into `b3ContactConstraintWide` structures:

```c
/* Example: converting a manifold into a wide contact constraint */
b3Manifold manifold;
b3CollideConvexConvex(shapeA, shapeB, &manifold);

/* Allocate a wide constraint for the pair */
b3ContactConstraintWide *wide = ctx->wideConstraints + wideIndex;
wide->normal = b3Vec3Widen(manifold.normal);
wide->depth = b3FloatWiden(manifold.depth);
wide->point0 = b3Vec3Widen(manifold.points[0]);
/* … repeat for up to 4 points … */

```

During the solve phase, the engine processes these blocks in SIMD-friendly loops:

```c
/* Example: processing a wide contact in the solver */
for (int i = 0; i < wideContactCount; ++i) {
    b3ContactConstraintWide *c = wideConstraints + i;
    b3Vec3 normal = b3Vec3Narrow(c->normal);
    float depth   = b3FloatNarrow(c->depth);
    /* SIMD-friendly impulse solve … */
}

```

## Performance Characteristics and Design Rationale

The coexistence of these structures addresses competing optimization constraints:

- **Manifold constraints** prioritize geometric accuracy. They store per-point positions in scalar form because collision detection requires precise world-space coordinates and sequential clipping algorithms.
- **Wide contacts** prioritize execution throughput. By packing data into vector registers, the solver achieves high performance on modern CPUs where SIMD width often matches or exceeds the typical contact point count between simple shapes.

According to the implementation in **src/contact_solver.c**, the solver consumes both representations: manifolds provide the persistent geometric truth for friction and restitution calculations, while wide contacts provide the cache-friendly layout necessary for impulse resolution at scale.

## Summary

- **Manifold constraints** (`b3Manifold`) store precise collision geometry including contact normals, penetration depths, and up to four contact points in scalar form according to **src/manifold.h**
- **Wide contacts** (`b3ContactConstraintWide`) repackage this data into 4-wide SIMD vectors for parallel processing across multiple constraints simultaneously
- Manifolds persist in the constraint graph (`b3ContactConstraintManifold`) for temporal coherence, while wide contacts are temporary stack allocations created per solver step in **src/solver.c**
- The conversion from manifolds to wide contacts occurs during the prepare/store phase, enabling the contact solver to achieve high throughput via contiguous memory layouts and `b3_wideContactBlock` iteration

## Frequently Asked Questions

### Why does Box3D maintain two separate contact representations?

Box3D uses dual representations because manifolds preserve the exact geometric detail needed for accurate collision response, friction modeling, and warm-starting across frames, while wide contacts provide the SIMD throughput necessary for real-time physics. This separation allows the narrow-phase to focus on geometric correctness using scalar algorithms in **src/manifold.c**, while the solver optimizes for parallel execution using vectorized blocks defined in **src/solver.h**.

### How many contact points can a manifold store?

Each `b3Manifold` structure defined in **src/manifold.h** can store up to four contact points (`b3Vec3 points[4]`). This accommodates the maximum contact count typically generated between intersecting convex shapes, such as face-face contacts between boxes or box-cylinder collisions.

### Where are wide contacts allocated during the simulation step?

Wide contacts are allocated on the physics world's stack in **src/solver.c** using `b3StackAlloc`. They exist only for the duration of the solver step and are automatically cleared afterward, unlike manifold constraints which persist in the constraint graph across simulation steps for temporal coherence.

### Can the solver process different collision types simultaneously using wide contacts?

Yes. The `b3ContactConstraintWide` structure uses vectorized data types that allow the solver to process four contact constraints simultaneously regardless of the underlying shape types. The `b3_wideContactBlock` iteration system groups constraints into contiguous blocks, maximizing SIMD lane utilization even when processing heterogeneous collision pairs (e.g., sphere-box and cylinder-cylinder contacts) within the same parallel-for loop.