Seeding and Determinism
Relevant source files
The following files were used as context for generating this wiki page:The @nextorders/avatar package ensures that avatar generation is deterministic. This is achieved by using a seed-based pseudo-random number generator (PRNG). For any given seed and set of options, the createAvatar function will produce the exact same SVG output every time. This is a critical feature for applications that require consistent and reproducible avatars for users.
The seed is a mandatory string parameter in the AvatarOptions. If it is not provided, the avatar generation process will fail. This design enforces the deterministic nature of the library.
Sources: packages/avatar/src/core/utils/prng.ts, packages/avatar/test/unit/index.test.ts:15-21, packages/avatar/src/index.ts:15
Core PRNG Implementation
The heart of the deterministic behavior lies in the PRNG implementation, which uses a string seed to initialize its state.
Sources: packages/avatar/src/core/utils/prng.ts
Seed Hashing and State Initialization
The create function in prng.ts is responsible for creating a new PRNG instance. It takes a string seed as input. This seed is first converted into a 32-bit integer hash using the hashSeed function. The hashSeed function iterates over the seed string, applying bitwise operations and an xorshift algorithm to create a numeric representation. This initial hash value becomes the internal state of the generator.
Sources: packages/avatar/src/core/utils/prng.ts:15-25, packages/avatar/src/core/utils/prng.ts:27-32
The diagram below illustrates the seed hashing process.
The xorshift Algorithm
The PRNG uses the xorshift algorithm to generate subsequent pseudo-random numbers. Each call to the next() method on a PRNG instance applies the xorshift function to the current internal state value to produce the next state, ensuring a deterministic sequence of numbers.
// packages/avatar/src/core/utils/prng.ts:7-13
function xorshift(value: number) {
value ^= value << 13
value ^= value >> 17
value ^= value << 5
return value
}
Sources: packages/avatar/src/core/utils/prngts:7-13
The Prng Interface
The create function returns an object that conforms to the Prng interface. This object provides several methods for generating different types of pseudo-random data, all derived from the initial seed.
The diagram below shows the structure of the Prng interface.
The table below describes the methods available on a Prng instance.
| Method | Description |
|---|---|
seed | The original string seed used to create the instance. |
next() | Advances the generator to its next state. This is the core of the sequence. |
bool(likelihood) | Returns true or false. The likelihood of returning true is based on the likelihood percentage (default 50). |
integer(min, max) | Returns a pseudo-random integer within the specified range (inclusive). |
pick(arr) | Selects and returns a random element from an array. |
shuffle(arr) | Returns a new array with the elements of the input array in a shuffled, deterministic order. It uses an internal PRNG instance to not interfere with the main sequence. |
string(length, chars) | Generates a pseudo-random string of a given length from a set of characters. It also uses an internal PRNG instance. |
Sources: packages/avatar/src/core/utils/prng.ts:34-92, packages/avatar/src/core/types.ts:10-18
Avatar Generation Flow
The seed is passed through the system to ensure every random decision is deterministic.
PRNG Instantiation
When createAvatar is called, it first builds a set of options and then passes them to the core createBaseAvatar function. Inside createBaseAvatar, the first step is to create a prng instance using the provided seed.
// packages/avatar/src/core/index.ts:11-12
export function createAvatar<O extends object>(
style: Style<O>,
options: StyleOptions<O> = {},
): Result {
// ...
const prng = createPrng(options.seed)
// ...
}
This single prng instance is then used for all subsequent random choices in the avatar generation process.
Sources: packages/avatar/src/core/index.ts:11-13, packages/avatar/src/index.ts:15-18
The following sequence diagram shows the flow from the initial call to the creation of the PRNG instance.
Deterministic Component Selection
The created prng instance is passed to the style.create method. In the case of the open-peeps style, this prng object is then used to deterministically select avatar components (like head, face, accessories) and colors.
For example, getComponents and getColors both receive the prng instance and use its pick and bool methods to make choices from the available options defined in the style schema. This ensures that the same seed always results in the same combination of facial features, hair, clothing, and colors.
The diagram below shows how the PRNG instance dictates component selection.
Usage and Testing
The library's tests confirm the importance and behavior of the seed.
A test exists to ensure that an error is thrown if createAvatar is called without a seed, enforcing the deterministic-by-design approach.
// packages/avatar/test/unit/index.test.ts:15-21
it('should return error if seed is not provided', () => {
try {
// @ts-expect-error seed is required
createAvatar({})
} catch (error) {
expect(error).toBeInstanceOf(Error)
}
})
Sources: packages/avatar/test/unit/index.test.ts:15-21
Other tests pass a specific seed and options, and then check for a specific SVG path in the output. This implicitly confirms that the output is deterministic and repeatable for a given seed.
// packages/avatar/test/unit/index.test.ts:23-26
it('should return correct result with emotion 5', () => {
const result = createAvatar({ seed: 'random4', emotion: 5 })
expect(result).toContain('m157.9 261 2-.5c7.7-2 15.7-5.4')
})
Sources: packages/avatar/test/unit/index.test.ts:23-26
Conclusion
Seeding is a cornerstone of the @nextorders/avatar library. By mandating a seed for avatar creation and using a robust PRNG system based on the xorshift algorithm, the library guarantees that every generated avatar is fully deterministic and reproducible. This core principle allows developers to reliably generate consistent avatars across different environments and sessions, which is essential for a stable user experience.