monomorph
v1.3.0
Published
Monomorph
Downloads
315
Readme
Monomorph is a Typescript and Javascript library for performance-critical applications. Monomorph was originally developed by Hamza Kubba to power Bounce, a free & open source physics library for the web. Monomorph also powers MECS, a free & open source ECS library.
With Monomorph, you can easily create classes that:
- have built-in object pooling to minimize garbage collection
- can nest and/or reference monomorph classes
- have built-in performant serialization/deserialization methods for data (including handling nesting and references), as well as other utility methods like .copy() which copies including nested object values.
- keep more methods monomorphic, avoiding potential slowdowns of up to 60x
- avoid repetitive boilerplate
- are strongly typed, when using Typescript
How it works
In a nutshell, Monomorph dynamically creates the JS code for a class based on the schema you provide, and you extend that class. Each generated class has unique helper methods that are not inherited, which avoids performance drops that result from class inheritance in JS. Read "Keep in Monomorphic" for more info about monomorphism, why it matters, and how this library helps.
Install into your project
npm install monomorphUsage
Basic class schema (Typescript)
Let's say we want to create a Vector2 class, which has x and y numbers on it. This is what that would look like in
import { createClass, NumberType, PropertyDefinitionMap } from 'monomorph';
const props = {
// x and y are numbers with default value 0
x: NumberType(0),
y: 0,
// ^^ this is shorthand for:
// y: NumberType(0)
} as const satisfies PropertyDefinitionMap;
class Vector2 extends createClass<Vector2, typeof props>(props) {
/* your methods here, for example add() */
add(otherVec2: Vector2) {
this.x += otherVec2.x;
this.y += otherVec2.y;
}
}import { createClass, NumberType } from 'monomorph';
const props = {
// x and y are numbers with default value 0
x: NumberType(0),
y: 0,
// ^^ this is shorthand for:
// y: NumberType(0)
};
class Vector2 extends createClass(props) {
/* your methods here, for example add() */
add(otherVec2) {
this.x += otherVec2.x;
this.y += otherVec2.y;
}
}Using Monomorph classes
// Vector2 defined above
const v1 = Vector2.create(); // x and y are 0
const v2 = Vector2.create({ x: 1, y: 2 });
const v3 = Vector2.create({ x: 3 }); // y is 0
v1.copy(v2); // built-in method, now v1 has the same values as v2
v1.add(v3); // custom method defined above
console.log(v1.x); // 4 (1 + 3)
console.log(v1.y); // 2 (2 + 0)Object pooling
Monomorph automatically creates a Pool class the class you create with it. For example, with the Vector2 class defined above, Vector2.Pool is the pool class. This is how you use it.
// Vector2 defined above
const vector2Pool = new Vector2.Pool();
// v1 is created inside vector2Pool
const v1 = Vector2.create({ x: 1, y: 2 }, vector2Pool);
// v2 is created in the same pool as v1 (also in vector2Pool)
const v2 = v1.createInPool({ x: 3, y: 4 });
// alternate syntax
const v3 = Vector2.create({ x: 5, y: 6 }, v1.pool);
for (const vector2 of vector2Pool) {
// loops over v1, v2, v3
}
// v2 is returned to the pool, and references to it that use ReferenceType(Vector2) will resolve as null
v2.destroy();
for (const vector2 of vector2Pool) {
// because we used v2.destroy(), this now loops over v1, v3
}
// creating v4 now will automatically reuses the v2 object
const v4 = Vector2.create({ x: 7, y: 8 }, vector2Pool);
for (const vector2 of vector2Pool) {
// loops over v1, v4, v3
}import { createClass, ChildType } from 'monomorph';
// Vector2 as defined above in the "Basic class schema" section
const boundsProps = {
min: ChildType(Vector2),
max: Vector2,
// ^^ this is shorthand for:
// max: ChildType(Vector2),
};
class Bounds extends createClass(boundsProps) {}
const bounds = Bounds.create({
min: { x: -1, y: -2 },
max: { x: 10, y: 5 },
});
const array = new Float64Array(); // can also be Float32Array or a basic js Array
aabb.toArray(array); // automagically generated method
const aabb2 = Aabb.create(); // create with default values
aabb2.fromArray(array); // now aabb2 has the same values as aabb
const aabb3 = Aabb.createFromArray(array); // same as aabb2 in one stepAdditional and advanced examples
There are many more examples written in Typescript in the tests folder, including BooleanType, ReferenceType, ReferenceListType and others, as well as handling the serialization and deserialization of pools that have references, and multiple pools to/from a single array.
Roadmap
- Add a generic non-monomorph type for holding references
- More official benchmarks; basic benchmarks were added in version 1.0.1 but more extensive benchmarks would be good
- Fixing bugs: while Monomorph is heavily used in our physics library Bounce, there may be bugs around edge cases and untested complex combinations. Please file an issue if you run into problems
- More supported types: Strings are partially implemented and not considered fully supported yet, other types will be added based on requests and code contributions
- Want to see something here? Please create an issue on codeberg
How to contribute
If you like this project and would like to support our work, please consider contributing code via pull requests, or donating via open collective. Contributions are greatly appreciated!
