saade/filament-facehash
Generate unique, deterministic SVG avatar faces for Filament panels. The same name always produces the same face. A drop-in replacement for default user avatars, no GD or Imagick needed, with customizable size, colors and variants plus a trait for Blade views and notifications.
size(), variant(), initial(), and colors() are product knobs, not just code: Solid for dense tables where an initial must stay legible, Gradient for the client-facing portal, palettes aligned to brand tokens. Different panels legitimately deserve different treatments, and this supports that without a design-system project.HasFacehashAvatar trait means the same face appears in Blade views, Inertia props, and in-app notifications—so the identity convention doesn't stop at the admin boundary.Where the PM has to own the tradeoff: determinism is both the feature and the footgun. Two "John Smith" accounts get identical faces; a rename silently produces a brand-new face. On a moderation or user-management screen where disambiguation is the job, this can actively mislead. Mitigate with a unique name-derivation policy (email or a per-ID fallback), pilot where collisions are rare, and document the one-line escape hatch before launch—not after.
Adopt when:
AvatarProvider (which is essentially all this adapter is).composer update can't silently reshuffle everyone's face.Look elsewhere—or build the twenty lines yourself—when:
img-src data: isn't negotiable. Check this before committing; it fails in production and passes in dev, which is the worst possible failure mode.For executives (30 seconds): A small, reversible fix for a visible quality gap. Today, users without a photo show as identical grey circles across our admin—which makes support screenshots and moderation harder to read. This gives each name a stable, on-brand generated face. No new infrastructure, no storage, no data migration, no ongoing ops. One line per panel, and it's a fallback—uploaded photos still win, so nothing existing changes. Reverting is deleting that line.
For engineering (the parts that matter): It's a presentation-layer fallback, not a data-layer commitment. It contributes an AvatarProvider implementation wired via ->defaultAvatarProvider(...), a plugin object carrying the fluent styling API, and a trait exposing facehash_avatar_url for Blade and notifications. Output is a pure-SVG data: URI generated in PHP at render time—no extensions, no config file to publish, no migrations. Rollout is one low-traffic panel first; exit cost is roughly one class if the package stalls, which defuses the single-maintainer concern. Two asks before merge: one determinism test plus one assertSee('data:image/svg+xml') integration assertion, and an explicit constraint pinning the underlying generator package—pinning the adapter alone does not pin the generator. Confirm img-src data: in the production CSP, and don't pass the data URI into queued notification constructors.
The honest caveats to state up front, not later: low adoption signals (few stars, no dependents), one maintainer, and determinism that cuts both ways—duplicate names collide and renames change the face. We're accepting that because the blast radius is a fallback avatar with a one-line rollback, and because the alternative is either living with grey circles or funding an upload pipeline nobody asked for.
How can I help you explore Laravel packages today?