Simulant is a small simulator toolkit for research on mobile entities and distributed algorithms.
For a quick presentation, public project links, and a short explanation of the simulator internals, start with the project page:
https://bramas.github.io/simulant/
This repository contains the reusable simulator core, the reusable web interface, and public paper artifacts.
apps/public-site/ public landing page and documentation site
packages/core/ reusable simulator core package
packages/simulator-ui/ reusable SolidJS simulator interface
projects/ public project and paper packages
projects/private/ local private projects, not published by the public site
Only projects listed in apps/public-site/src/public-projects.mjs are published on the website.
pnpm installpnpm dev # run the starter ring project locally
pnpm build # generate the full public static site in dist/For a separate project, depend on the published simulator packages. With GitHub Packages, add this .npmrc:
@bramas:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}Then use the released packages:
{
"dependencies": {
"@bramas/simulant-core": "0.1.0",
"@bramas/simulant-ui": "0.1.0",
"solid-js": "^1.9.3"
}
}During local development, you can point a private project to a local simulator checkout instead of publishing temporary versions:
{
"dependencies": {
"@bramas/simulant-core": "link:../simulant/packages/core",
"@bramas/simulant-ui": "link:../simulant/packages/simulator-ui",
"solid-js": "^1.9.3"
}
}Switch back to exact published versions when a paper artifact must be frozen.
This example uses the ring model from @bramas/simulant-core@0.1.0. A project exports a projects map; each entry exposes a setup function that returns a model instance.
// projects.ts
import { Config } from '@bramas/simulant-core/lib/system';
import Ring, {
Actions,
AbstractGraphAgent,
type RingConfigType,
} from '@bramas/simulant-core/models/ring';
import type { ProjectType } from '@bramas/simulant-core/types/project';
type Memory = {
moving: boolean;
};
class Walker extends AbstractGraphAgent<Memory> {
moving = true;
constructor(id: number, position: number) {
super(id, position);
}
visibleMemory(): Memory {
return { moving: this.moving };
}
action_simpl(view: Memory[]): Actions {
return view.length > 0 ? Actions.IDLE : Actions.CLOCKWISE;
}
clone(): Walker {
const clone = new Walker(this._id, this._pos);
clone.moving = this.moving;
return clone;
}
}
type MyConfig = RingConfigType<Walker, Memory, null>;
export const projects = {
'basic-ring-walk': {
setup: (settings: { N?: number }) => {
const N = settings.N ?? 8;
const agents = [new Walker(0, 0), new Walker(1, 3)];
const initialConfig = new Config() as MyConfig;
for (const agent of agents) {
initialConfig.addAgent(agent._pos, agent);
}
return Ring(N, agents, initialConfig);
},
editorSchema: {
fields: [{ key: 'N', label: 'Ring size', type: 'number', defaultValue: 8, min: 3 }],
},
},
} satisfies Record<string, ProjectType<MyConfig>>;For a web page using the default simulator UI:
// web/src/App.tsx
import { SimulatorApp } from '@bramas/simulant-ui/SimulatorApp';
import { projects } from '../../projects';
export default function App() {
return (
<SimulatorApp
title="Basic Ring Walk"
projects={projects}
createGraphWorker={() =>
new Worker(new URL('./workers/graph-worker.ts', import.meta.url), { type: 'module' })
}
storageKey="basic-ring-walk-settings-v1"
/>
);
}// web/src/workers/graph-worker.ts
import { execute } from '@bramas/simulant-core/lib/system';
import { projects } from '../../../projects';
self.onmessage = (event) => {
const { project, settings, path } = event.data;
const model = projects[project].setup(settings);
const graph = execute(model);
self.postMessage({
config: graph.get(path),
childCount: graph.getChildrenCount(path),
siblingCount: graph.getSiblingCount(path),
});
};In practice, most papers also add drawConfig, getAgentsRenderInfo, executionStatusChecker, and presets, but the core contract is the same: define agents, choose a model, return a SystemInfo.
The public site is generated by scripts/build-public-site.mjs. It builds every public project listed in apps/public-site/src/public-projects.mjs, then builds apps/public-site/ into dist/.
GitHub Pages deployment is configured in .github/workflows/pages.yml. In the GitHub repository settings, set Pages source to GitHub Actions.
.github/workflows/release-packages.yml publishes:
@bramas/simulant-core@bramas/simulant-ui
It runs manually from GitHub Actions or when pushing a tag matching simulator-v*. Before tagging a release, bump the package versions in packages/core/package.json and packages/simulator-ui/package.json.
Simulant is developed by Quentin Bramas.
This software is distributed under the CeCILL-B free software license. See LICENCE.txt.
If you use this simulator or the black-hole-search artifact, please cite:
François Bonnet, Quentin Bramas, and Anissa Lamani. Searching for an Eventually-Emerging Black Hole in Rings. In 5th Symposium on Algorithmic Foundations of Dynamic Networks (SAND 2026).
See PUBLICATION.md for the versioning strategy used when publishing paper code.