npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bigpuddle/dot-engine-brand

v0.2.3

Published

Brand identity layer for dot-engine — defineBrand, logo import, personality, contexts, particles

Readme

@dot-engine/brand

Brand identity layer for dot-engine. Wraps the core DAG API with brand-level concepts: logo import, personality mapping, motion styles, brand contexts, particles, and data visualization.

npm install @dot-engine/brand

Peer dependencies: @dot-engine/renderer, react, three, @react-three/fiber.


defineBrand(config: BrandConfig): Promise<Brand>

The main entry point. Returns a Brand object that can generate a FieldRoot for any context with brand.field().

import { defineBrand, text } from '@dot-engine/brand';

const brand = await defineBrand({
  name: 'Acme Corp',
  logo: text('ACME'),
  colors: {
    primary: '#4a9eff',
    accent: '#ff6b4a',
    background: '#06060a',
  },
  personality: { energy: 0.6, organic: 0.7, density: 0.5 },
  motion: { style: 'flow', speed: 0.4 },
});

BrandConfig

| Field | Type | Description | |---|---|---| | name | string | Optional brand name (metadata only) | | logo | LogoInput | Logo source — see Logo import below | | colors.primary | string | Primary hex color | | colors.accent | string | Accent hex color | | colors.background | string | Canvas background hex color | | personality | PersonalityTraits | Energy, organic, density 0–1 each | | motion.style | MotionStyle | 'flow' \| 'breathe' \| 'pulse' \| 'none' | | motion.speed | number | Base speed multiplier |

Brand

The resolved brand object returned by defineBrand.

| Member | Type | Description | |---|---|---| | config | BrandConfig | Original config | | logo | ProcessedLogo | Processed logo data (SDF texture, aspect ratio) | | field(context?, options?) | FieldRoot | Generate a field for the given context | | particles(mode) | ParticleNode \| null | Generate a particle node |


Logo import

svg(source: string): SvgInput

Import an SVG from a URL or inline SVG string.

import { svg } from '@dot-engine/brand';
logo: svg('/logo.svg')
logo: svg('<svg>...</svg>')

image(source: string): ImageInput

Import a raster image from a URL.

import { image } from '@dot-engine/brand';
logo: image('/logo.png')

text(text: string, opts?): TextInput

Render text as a logo using a 2D canvas.

import { text } from '@dot-engine/brand';
logo: text('ACME')
logo: text('ACME', { font: 'Georgia', weight: 700 })

| Option | Type | Default | Description | |---|---|---|---| | font | string | system sans-serif | Font family name | | weight | number | 700 | Font weight |

importLogo(input: LogoInput, opts?): Promise<ProcessedLogo>

Low-level logo import — used internally by defineBrand.

| Option | Type | Default | Description | |---|---|---|---| | resolution | number | 256 | SDF texture resolution | | depth | number | 0.1 | SDF extrusion depth |


Personality system

Three parameters in [0, 1] jointly control every physical parameter of the dot field:

| Trait | Effect | |---|---| | energy | Animation speed, displacement amount. Low = still and precise; high = fast and chaotic. | | organic | Edge softness, whether curl (flow field) noise is used. Low = sharp, mechanical; high = soft, fluid. | | density | Grid resolution. Low = sparse, visible dots; high = dense, fills the form. |

These map to:

| Derived param | Formula | |---|---| | animateSpeed | lerp(0.05, 0.8, energy) | | displacementAmount | lerp(0.02, 0.15, energy) | | edgeSoftness | lerp(0.02, 0.1, organic) | | useFlowField | organic > 0.5 | | gridResolution | round(lerp(20, 60, density)) |


Motion styles

| Style | Displacement used | Character | |---|---|---| | flow | Simplex3D + optional FlowField3D (curl noise) | Smooth, continuous, organic | | breathe | Slow simplex3D (0.5× amount, 0.2× speed) | Slow, gentle, inhale/exhale | | pulse | Fast simplex3D (1.2× amount, 2× speed) | Sharp, rhythmic, electric | | none | No displacement | Static, frozen |

The flow field is included only when organic > 0.5 (controlled by useFlowField).


Brand contexts

brand.field(context, options?) returns a FieldRoot tuned for the given display context.

| Context | Description | |---|---| | logo | Square or aspect-ratio-correct logo lockup. Thin Z depth. | | hero | Full-bleed widescreen version. Deeper Z, slower motion. | | loading | Compact, looping breathing animation. Forces breathe style. | | banner | Very wide aspect (4:1+). Reduced displacement for readability. | | data | Data visualization — add DataPoint[] via options.data. | | transition | Cross-fade between two contexts. Use with BrandMoment. |

ContextOptions

| Option | Type | Description | |---|---|---| | width | number | Output width hint | | height | number | Output height hint | | canvasAspect | number | Canvas width/height ratio for grid adaptation | | data | DataPoint[] | Data points for data context | | from | BrandContext | Source context for transition | | to | BrandContext | Target context for transition | | progress | number | Transition progress 0–1 | | twist | number | SDF twist transform amount | | bend | number | SDF bend transform amount | | mirrorX | boolean | Mirror SDF across X axis | | mirrorY | boolean | Mirror SDF across Y axis | | dotSizeMin | number | Override minimum dot radius | | dotSizeMax | number | Override maximum dot radius | | edgeSoftness | number | Override edge softness |

// Hero context adapted to a 16:9 canvas
const heroField = brand.field('hero', { canvasAspect: 16 / 9 });

// Banner
const bannerField = brand.field('banner');

// Transition from logo to hero at 40%
const transField = brand.field('transition', {
  from: 'logo', to: 'hero', progress: 0.4
});

<BrandMoment>

React component that renders a brand at a context. Handles the transition cross-fade internally.

import { BrandMoment } from '@dot-engine/brand';

<BrandMoment
  brand={brand}
  context="logo"
  options={{ canvasAspect: 2 }}
  interactive={true}
/>

Props (BrandMomentProps)

| Prop | Type | Default | Description | |---|---|---|---| | brand | Brand | required | Brand object from defineBrand | | context | BrandContext | 'logo' | Display context | | options | ContextOptions | — | Context options | | lod | 'auto' \| LodOverride | 'auto' | LOD tier | | className | string | — | CSS class on the wrapper | | style | React.CSSProperties | — | Inline styles | | interactive | boolean | true | Enable OrbitControls |

For context="transition", BrandMoment renders two overlaid DotFieldCanvas instances and drives their opacity from options.progress.


Particles

buildParticles(mode, config, params): ParticleNode | null

Builds a ParticleNode tuned to the brand's personality.

const particleNode = brand.particles('rising');
// returns null for mode 'none'

ParticleMode

| Mode | Description | |---|---| | none | No particles | | ambient | Low-rate drift particles from origin, long lifetime | | burst | One-shot burst of fast particles with gravity | | rising | Continuous upward stream from the bottom | | edges | Surface-emitting particles that drift outward |

particlePresets

Pre-built particle nodes using neutral default brand parameters:

import { particlePresets } from '@dot-engine/brand';

particlePresets.ambientDrift  // ambient mode
particlePresets.burst         // burst mode
particlePresets.rising        // rising mode
particlePresets.edges         // edges mode

Data visualization

The data context places attract displacement nodes at each data point, pulling the dot field toward high-value regions.

DataPoint

| Field | Type | Description | |---|---|---| | position | [x,y,z] | Normalized position in [0,1]³, mapped to world [-1,1]³ | | value | number | Influence strength (0–1 recommended) | | radius | number | Unused in current implementation (reserved) | | category | string | Unused in current implementation (reserved) |

const dataField = brand.field('data', {
  data: [
    { position: [0.5, 0.8, 0.5], value: 0.9 },
    { position: [0.2, 0.3, 0.5], value: 0.4 },
    { position: [0.8, 0.6, 0.5], value: 0.7 },
  ],
});

Up to 16 data points are processed. Each creates an attract displacement with falloff: 'inverse'.


Image field utilities

import { loadImageForField, grabVideoFrame } from '@dot-engine/brand';

loadImageForField(source, resolution?): Promise<ImageFieldData>

Loads an image URL into a Float32Array of RGBA pixels. Browser-only (uses HTMLImageElement and CanvasRenderingContext2D).

| Parameter | Type | Default | Description | |---|---|---|---| | source | string | required | Image URL | | resolution | number | 256 | Output texture width in pixels |

grabVideoFrame(video, resolution?): ImageFieldData

Captures one frame from an HTMLVideoElement into a Float32Array. Browser-only.

ImageFieldData

| Field | Type | Description | |---|---|---| | pixels | Float32Array | RGBA data in [0,1] range | | width | number | Pixel width | | height | number | Pixel height | | textureId | string | Unique ID for texture binding |


Switching contexts

import { useState } from 'react';
import { BrandMoment } from '@dot-engine/brand';

function BrandDisplay({ brand }) {
  const [ctx, setCtx] = useState('logo');

  return (
    <div style={{ width: 800, height: 400 }}>
      <BrandMoment brand={brand} context={ctx} />
      <button onClick={() => setCtx('hero')}>Hero</button>
      <button onClick={() => setCtx('loading')}>Loading</button>
    </div>
  );
}

For animated transitions, manage a progress state and pass context="transition":

<BrandMoment
  brand={brand}
  context="transition"
  options={{ from: 'logo', to: 'hero', progress }}
/>