# Introduction

> dirtytalk is a set of dirty-tracking primitives that answer what changed, who cares, and when to tell them, for any domain.

Source: https://dirtytalk.42.dog/docs/

dirtytalk is a small monorepo of dirty-tracking primitives. After a mutation, it computes what changed once, tells only the consumers that care, and does so at a moment you choose.

## What dirtytalk does

After any mutation, three questions need answers:

1. **What changed?** A `Space` defines what a region is in your domain (a set of keys, a set of paths, a list of rectangles). Every mark is a region, and marks within one window are unioned into a single dirty region.
2. **Who cares?** Each subscriber supplies an interest thunk that returns a region. On flush, only subscribers whose interest intersects the dirty region are called.
3. **When do we tell them?** A `Scheduler` decides when the flush runs: immediately, at the end of the microtask, on the next animation frame, or when you call `pump()`.

See [Concepts](/docs/concepts/) for the full model.

## Packages

| Package                 | Role                                                                                                             | Docs                            |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| `@dirtytalk/engine`     | The algebra and the glue: `Space<Region>`, `Scheduler` and its four implementations, and `DirtyChannel`.         | [Engine](/docs/engine/)         |
| `@dirtytalk/structural` | A `Space` over interned state paths (`PathSet`), plus a state container, render tracking and a React hook.       | [Structural](/docs/structural/) |
| `@dirtytalk/spatial`    | A `Space` over damage rectangles (`Damage[]`), plus a scene graph, a damage-driven renderer and pointer routing. | [Spatial](/docs/spatial/)       |

`structural` and `spatial` are built on `engine`. Neither re-implements scheduling or subscription.

## Which package do I need

- You need to coalesce marks and notify interested subscribers for your own kind of region: use `@dirtytalk/engine` and implement a `Space`. See [Custom spaces](/docs/engine/custom-spaces/).
- You have plain object state and want consumers (such as React components) to re-run only when the paths they read change: use `@dirtytalk/structural`.
- You draw to a canvas or similar surface and want to repaint only the rectangles that changed: use `@dirtytalk/spatial`.
- You only need the scheduling behavior (sync, microtask, animation frame, manual): use `@dirtytalk/engine` alone.

## Example

A channel over a set of string keys, using `@dirtytalk/engine` only. Marks in one microtask coalesce into a single flush, and subscribers whose interest does not intersect the dirty region are skipped.

```ts
import { DirtyChannel, MicrotaskScheduler } from '@dirtytalk/engine';
import type { Space } from '@dirtytalk/engine';

const KeySpace: Space<Set<string>> = {
  empty: () => new Set(),
  isEmpty: (r) => r.size === 0,
  union: (a, b) => new Set([...a, ...b]),
  intersects: (interest, dirty) => {
    for (const key of interest) if (dirty.has(key)) return true;
    return false;
  },
};

const channel = new DirtyChannel(KeySpace, new MicrotaskScheduler());

channel.subscribe(
  () => new Set(['users']),
  (dirty) => console.log('users view:', [...dirty]),
);
channel.subscribe(
  () => new Set(['theme']),
  (dirty) => console.log('theme view:', [...dirty]),
);

channel.mark(new Set(['users']));
channel.mark(new Set(['session']));

await Promise.resolve();
// users view: [ 'users', 'session' ]
// (theme view is not called: its interest did not intersect)
```

## Next steps

- [Installation](/docs/installation/): install each package, entry points, and versioning.
- [Concepts](/docs/concepts/): the model shared by all three packages.
- Interactive chapters: [engine](/learn/engine/), [structural](/learn/structural/), [spatial](/learn/spatial/).
