Skip to content

Architecture

Flare is a TypeScript-first 2D engine for React Native and Web — for animation, gamification, interactive UI, and games. It ships as a set of small, single-purpose packages with strict structural boundaries — you depend only on the layers you use, and lower layers never reach up into higher ones.

This page is a summary. The canonical, always-current source of truth lives in the engine repo’s design docs, linked at the bottom; for the live list of every package, see the package catalog.

The engine is organized into layers, not tiers. There are seven numbered layers (0–6) plus two half-layers (2.5 and 3.5), for nine bands total. The example packages below are illustrative — the package catalog is the exhaustive, always-current list.

Layer 0 — Foundation

Zero dependencies — e.g. math, events.

Layer 1 — Core

Core runtime: loop, clock, ECS, pooling — core, ecs.

Layer 2 — Systems

System modules — e.g. render, input, physics, audio, assets, simulation, progression, shapes.

Layer 2.5 — Persistence

Storage, reliability, errors — e.g. storage, errors, testing.

Layer 3 — Features

Feature + genre modules — e.g. scene, animation, particles, pathfinding, dialogue.

Layer 3.5 — UI & post-FX

UI and post-processing — ui, postfx.

Layer 4 — Integration

React + web bindings and the umbrella — react, render-web, engine.

Layer 5 — Tooling

CLIs and devtools, off the device budget — cli, devtools, create-flare-app.

Layer 6 — Platform integrations

Provider interfaces + adapters — leaderboards, achievements, iap, analytics, haptics.

The shipping surface is currently 41 packages across the nine bands above — the @flare-engine/* scope plus the unscoped create-flare-app scaffolder (Layer 5). The engine is under active development, so this number moves — the package catalog is generated from the engine and kept current by a weekly sync, so it never goes stale.

You don’t install all of them. Most apps pull the umbrella package plus the React bindings; see Getting Started for the install path.

The defining rule of the architecture:

Lower layers never import from higher layers.

Dependencies flow in one direction only — down the layer stack. A Layer 1 core package can never reach into a Layer 3 feature. This keeps the graph acyclic, makes individual packages testable in isolation, and lets bundlers tree-shake everything you don’t use (the one deliberate exception is the umbrella barrel, below).

Flare renders through Skia, with two backends behind one API:

Native (iOS + Android)

@shopify/react-native-skia — GPU rendering via the Atlas API and Canvas. Required by @flare-engine/render and @flare-engine/react.

Web (browser)

@flare-engine/render-web — CanvasKit WASM, the same Skia drawing model compiled to WebAssembly for the browser. Provides loadCanvasKit(), createWebSurface(), and a post-FX compositing surface. (This site’s hero runs a local precursor of it while the published-package dogfood is in progress.)

The same codebase targets iOS, Android, and the browser. The native path leans on Skia’s single-draw-call <Atlas> batching and runs sprite transforms on the UI thread via useRSXformBuffer for zero FFI cost.

@flare-engine/engine (Layer 4) is the umbrella barrel. It provides the createGame() convenience factory and re-exports all the lower packages, so you can install one dependency and reach the whole engine. It is the one package that is intentionally not tree-shakeable — pull from the individual @flare-engine/* packages when you want maximum shaking.

The companion Layer-4 package, @flare-engine/react, supplies the React/React Native component surface (<GameView>, hooks, virtual controls). It is not the umbrella — engine re-exports the lower layers, react owns the runtime host and Skia canvas lifecycle. Most apps install both.

Per-frame code runs inside a tight budget, so the engine is built to avoid garbage-collector pressure in hot loops:

  • Vec2 as plain { x, y } objects, not class instances — avoids GC pressure in hot loops.
  • vec2(x, y) factory plus a static Vec2.add(a, b, out?) form — the out? parameter is the zero-alloc seam: reuse an output object instead of allocating a new one each call.
  • Object pooling via ObjectPool / Pool in @flare-engine/core.
  • Hybrid ECS — object entities with an optional struct-of-arrays typed-array escape hatch for hot components.
  • __DEV__ guards wrap debug-only code so it tree-shakes out of production builds.
  • Pre-create queries in setup — never call world.query() per frame; it allocates.

These summaries track the engine’s design docs. Read them for the full layer diagram, module summary, dependency tables, and design decisions: