A type that should have many instances (every glyph in a document, every tile on a map, every projectile in a scene) carries enough per-instance state that memory scales linearly with instance count. Most of that state — texture, mesh, font metrics — is identical across instances of the same kind.
Intrinsic state (the kind-shared data) lives in one Flyweight object per kind; extrinsic state (the per-instance data: position, scale, custom parameters) lives on the instance. A registry hands out the shared Flyweight; instances reference it rather than copy it.
Before the pattern
class Tree {constructor(species, x, y, age, scale) {this.species = species;this.texture = loadTexture(species);this.mesh = loadMesh(species);this.animation = loadAnimation(species);this.x = x;this.y = y;this.age = age;this.scale = scale;}render(ctx) {/* uses this.texture, this.mesh, this.x, this.y */}}const forest = [];for (let i = 0; i < 10000; i++) {forest.push(new Tree('oak', randomX(), randomY(), randomAge(), 1.0));}// 10000 × 7MB of texture/mesh data per tree = catastrophic memory cost.
After the pattern
class TreeType {constructor(species) {this.species = species;this.texture = loadTexture(species);this.mesh = loadMesh(species);this.animation = loadAnimation(species);}render(ctx, x, y, age, scale) {/* uses this.texture, this.mesh, x, y, scale */}}class TreeTypeRegistry {static cache = new Map();static get(species) {if (!TreeTypeRegistry.cache.has(species)) {TreeTypeRegistry.cache.set(species, new TreeType(species));}return TreeTypeRegistry.cache.get(species);}}class Tree {constructor(species, x, y, age, scale) {this.type = TreeTypeRegistry.get(species);this.x = x;this.y = y;this.age = age;this.scale = scale;}render(ctx) {this.type.render(ctx, this.x, this.y, this.age, this.scale);}}// 10000 Trees × ~40 bytes each + one shared TreeType per species.
Per-instance copies of shared state are Duplicated Code in memory form — N×M bytes when 1×M would suffice, where N is instance count and M is intrinsic-state size. The cost is invisible during development on small datasets and catastrophic when the dataset reaches realistic scale.
Splitting intrinsic from extrinsic state forces every method on the instance to receive the extrinsic state explicitly (as args or as `this.x`-style fields) rather than reading it from intrinsic-side fields. Methods on the Flyweight take more parameters; client code at every call site provides them.
Memory cost drops from N×M to N + M; load time drops because each kind loads once; instance construction becomes a registry lookup + per-instance field set. Tests of the intrinsic state run once per kind, not per instance.
Flyweight applied where extrinsic state is mutable and the instance count is small adds indirection without paying for itself. The agent reads `tree.type.render(ctx, this.x, ...)` and must follow two hops to understand what render actually does, costing context budget that the memory savings can't justify at small N.