14 KiB
Contributing
Thanks for your interest in contributing to DiceBear.
This is the main monorepo: the JavaScript, PHP, Python, Rust, Go, and Dart core
libraries, the CLI, the docs site, and the editor all live here. Repositories
covering the JSON Schema, the avatar style definitions, the HTTP API, and the
Figma exporter are separate and each have their own CONTRIBUTING.md:
dicebear/schema: JSON Schema for definitions and optionsdicebear/styles: Official avatar style definitionsdicebear/api: Self-hostable HTTP APIdicebear/exporter-plugin-for-figma: Figma plugin
If your contribution belongs to one of those repos, read its file first. The instructions below only cover this monorepo.
Before you start
- Bug fixes, small improvements, new tests: open a pull request against the
branch that matches the target major (for DiceBear 10 that's
10.x; the current stable line lives on9.x). - New avatar styles: contribute them to
dicebear/styles, not here. The walkthrough is in Create an avatar style with Figma or Create an avatar style from scratch. - Larger changes (new public API, rendering behavior, breaking changes): open an issue first so we can agree on scope before you spend time on it.
- Security issues go privately to contact@dicebear.com; never file a public issue for a vulnerability.
- Contributors follow the DiceBear Code of Conduct.
Requirements
- Node.js 20 or newer (CI runs on 20, 22, 24, 25)
- npm 11 (this repo pins
packageManagerinpackage.json; use Corepack if you don't already) - For PHP work: PHP 8.2+ and Composer, plus
vendor/bin/phpunitviacomposer installinsidesrc/php/core/ - For Python work: Python 3.10+ (CI runs on 3.10 to 3.14); install the package
in a virtualenv with
pip install -e ".[dev]"insidesrc/python/core/ - For Rust work: Rust 1.80+ with the
clippyandrustfmtcomponents; build and test withcargo test,cargo clippy, andcargo fmtinsidesrc/rust/core/ - For Go work: Go 1.23+ (CI runs on 1.23 to 1.25); test with
go test ./...and check withgofmt -l .andgo vet ./...insidesrc/go/core/ - For Dart work: Dart SDK 3.4+ (CI runs on 3.4 and stable); test with
dart testand check withdart format --output=none --set-exit-if-changed .anddart analyze --fatal-infosinsidesrc/dart/core/
Local setup
git clone git@github.com:dicebear/dicebear.git
cd dicebear
npm install
The monorepo uses npm workspaces (src/js/* and apps/*) driven by
Turborepo. A single install at the root is enough; do
not npm install inside individual packages.
Common scripts
Run these from the repo root:
| Script | What it does |
|---|---|
npm run build |
Builds every workspace via Turbo |
npm test |
Runs every workspace's test target |
npm run test:scripts |
Runs the repo-level scripts in tests/**/*.test.js |
npm run lint |
Runs ESLint across the repo with caching |
npm run lint:fix |
Same, with --fix |
npm run prettier |
Formats src/ and apps/ with Prettier |
For faster loops, scope to a single workspace:
npm run build --workspace @dicebear/core
npm run test --workspace @dicebear/core
Repository layout
src/
├── js/ # TypeScript packages published to npm
│ ├── core/ # Rendering engine (@dicebear/core)
│ ├── cli/ # Command-line interface
│ └── converter/ # SVG → raster converter
├── php/ # PHP port (Composer package `dicebear/core`)
├── python/ # Python port (PyPI package `dicebear-core`)
├── rust/ # Rust port (crates.io crate `dicebear-core`)
├── go/ # Go port (module `github.com/dicebear/dicebear-go/v10`)
└── dart/ # Dart port (pub.dev package `dicebear_core`)
apps/
├── docs/ # VitePress documentation site (dicebear.com), including the Playground
└── editor/ # The in-browser editor (editor.dicebear.com)
tests/
└── fixtures/parity/ # Cross-language parity fixtures (see below)
scripts/
└── version.mjs # Bumps versions across the workspace and tags
Working on a package
TypeScript packages (src/js/*)
npm run build --workspace <package>
npm run test --workspace <package>
If you are working on the CLI, call the compiled script directly once you've built it:
node src/js/cli/bin/index.js <command>
PHP core (src/php/core/)
cd src/php/core
composer install
vendor/bin/phpunit
Python core (src/python/core/)
cd src/python/core
pip install -e ".[dev]" # in a virtualenv
ruff check .
ruff format --check .
mypy src
pytest
The Python core reads the two draft-07 schemas from the dicebear-schema
package (the Python counterpart of @dicebear/schema / dicebear/schema),
which pip install -e ".[dev]" pulls in as a runtime dependency. Nothing is
vendored.
Rust core (src/rust/core/)
cd src/rust/core
cargo test
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --check
The Rust core reads the two draft-07 schemas from the dicebear-schema crate
(the Rust counterpart of @dicebear/schema / dicebear/schema) as a runtime
dependency. Nothing is vendored.
Go core (src/go/core/)
cd src/go/core
go test ./...
go vet ./...
gofmt -l . # prints nothing when formatting is correct
The Go core reads the two draft-07 schemas from the github.com/dicebear/schema
module (the Go counterpart of @dicebear/schema / dicebear/schema) as a
dependency. Nothing is vendored. Style definitions come from
github.com/dicebear/styles/v10. The module path carries the major version
(/v10), so a major bump changes the path by hand; scripts/version.mjs only
creates the Git tag the module proxy reads.
Dart core (src/dart/core/)
cd src/dart/core
dart pub get
dart test
dart analyze --fatal-infos
dart format --output=none --set-exit-if-changed .
The Dart core reads the two draft-07 schemas from the dicebear_schema package
(the Dart counterpart of @dicebear/schema / dicebear/schema) as a runtime
dependency, validated with package:json_schema. Nothing is vendored.
src/dart/core/CHANGELOG.md is a git-ignored build artifact: pub.dev expects a
changelog inside the package directory, so the CI workflows copy the repository
root CHANGELOG.md there before dart pub publish [--dry-run]. This follows
the same pattern as the generated LICENSE copies in the styles and schema
repositories. Maintain the root changelog only.
Cross-language parity
Every port must produce output byte-identical to the reference JavaScript
core (@dicebear/core) for the same inputs. A shared fixture suite in
tests/fixtures/parity/ (generated from the JS reference) enforces this, and
each side consumes it:
- JS side:
src/js/core/tests/Parity.test.js, run vianpm run test --workspace @dicebear/core. - PHP side:
tests/ParityTest.php, run viavendor/bin/phpunitinsrc/php/core/. - Python side:
tests/test_parity.py, run viapytestinsrc/python/core/. - Rust side: the module unit tests plus
tests/avatars.rs, run viacargo testinsrc/rust/core/. - Go side: the in-package tests (
parity_test.go,avatars_test.go), run viago test ./...insrc/go/core/. - Dart side: the tests under
test/parity/, run viadart testinsrc/dart/core/.
The fixtures cover Fnv1a (hash + hex), Mulberry32 (chained sequences), every
Prng method, number-to-string formatting (numbers.json, the formatNumber
contract: every emitted number rounded to at most 5 decimal places, which every
port must reproduce identically), initials extraction, the Color helpers
(including bit-exact luminance doubles), accept/reject validation outcomes plus
circular color-reference chains, the OptionsDescriptor field map, and full
Avatar.toString() (plus selected toDataUri()) output for the initials,
thumbs, glass, notionists, and shape-grid styles. That last bucket
covers seed, size, transforms, gradients, title escaping, and
component-variant overrides.
Float determinism is part of the parity contract: pow is not correctly rounded
(JS engines, libm, and Go all differ in the last bit), so the sRGB linearization
ships as a precomputed 256-entry table in every port, and languages that fuse
multiply-add into FMA instructions (e.g. Go on arm64) must force intermediate
rounding. The details live in the
Implement DiceBear Core
spec.
If your change affects rendering or the PRNG in @dicebear/core, regenerate the
fixtures from the JS reference and commit the diff:
npm run fixtures:parity
The PHP, Python, Rust, Go, and Dart suites will then fail loudly until those sides are brought back in sync. That is the intended signal. If you only intend to touch one language, expect to update both before your PR can be merged.
When porting DiceBear to another language, run these fixtures against your implementation to prove it conforms. See Implement DiceBear Core for the full spec.
Documentation changes (apps/docs/)
The docs site is a VitePress app under apps/docs/.
npm run dev --workspace @dicebear/docs # live reload on localhost
npm run build --workspace @dicebear/docs # production build check
For larger editorial changes, open a draft PR early so reviewers can follow along.
Editor changes (apps/editor/)
The editor is the standalone app served at editor.dicebear.com.
npm run dev --workspace @dicebear/editor
npm run build --workspace @dicebear/editor
Code style
- ESLint and Prettier decide what counts as correctly formatted code; run
npm run lintandnpm run prettierbefore you open a PR. - TypeScript is
strict. Prefer narrow types toany/unknowncasts. - PHP code follows PSR-12;
vendor/bin/phpunitand Composer's built-in scripts catch the rest. - Python code is formatted and linted with Ruff
and type-checked with
mypyin strict mode; runruff check .,ruff format ., andmypy srcinsrc/python/core/before you open a PR. - Rust code is formatted with
rustfmtand linted with Clippy (warnings denied); runcargo fmtandcargo clippy --all-targets --all-features -- -D warningsinsrc/rust/core/before you open a PR. - Go code is formatted with
gofmtand vetted withgo vet; rungofmt -w .andgo vet ./...insrc/go/core/before you open a PR. - Dart code is formatted with
dart formatand analyzed withdart analyze --fatal-infos(lints fromanalysis_options.yaml); run both insrc/dart/core/before you open a PR.
Releasing (maintainers only)
Only maintainers with write access can release new versions. This section is documented here for completeness.
Releases are triggered by Git tags. The version script updates every package in the workspace, creates a commit, and creates the tag:
node scripts/version.mjs <version>
The version must be a valid semver value (e.g. 10.1.0
or 10.2.0-alpha.1). The script will:
- Update
versionin everypackage.jsonacross the workspace - Update internal workspace dependency references
- Update
versioninsrc/python/core/pyproject.toml,src/rust/core/Cargo.toml, andsrc/dart/core/pubspec.yaml(the Python, Rust, and Dart cores are not npm workspaces, so they are bumped explicitly to stay in lockstep) - Sync
package-lock.json - Create a Git commit and tag (e.g.
v10.1.0)
Push the commit and the tag:
git push && git push --tags
The tag triggers the Publish workflow, which:
- Runs the test suite on Node 20, 22, 24, and 25
- Builds every package
- Picks the npm dist-tag:
- Tags containing
alpha,beta, orrcgo out asnext - Other tags on the default branch go out as
latest - Other tags on any other branch go out as
v<major>-lts(so that backporting a patch to9.xafter10.xhas shipped does not overwritelatest)
- Tags containing
- Publishes all changed packages to npm via
scripts/publish.mjs - Builds
src/python/coreand publishesdicebear-coreto PyPI via trusted publishing (thepublish-pythonjob): no token and no separate repository, the same way the npm packages go out - Publishes the Rust core
dicebear-coreto crates.io via trusted publishing (thepublish-rustjob): likewise no token;cargo publishbuilds and uploadssrc/rust/corein one step - Publishes the Dart core
dicebear_coreto pub.dev via automated publishing (thepublish-pubjob): likewise no token; pub.dev verifies thev<version>tag against the pubspec version and publishessrc/dart/corestraight from the monorepo
The PHP port is the exception: Composer/Packagist consumes one Git repository
per package rather than a monorepo subdirectory, so split-php-core.yml mirrors
src/php/core (tags included) to the standalone
dicebear/dicebear-php repository,
and Packagist publishes dicebear/core from that mirror. All five ports ride
the same version the monorepo tagged.
Licensing
By opening a pull request you agree that your contribution is released under the
repository's MIT license. Avatar style artwork in dicebear/styles
may carry other licenses; see that repo's LICENSE.md for details.