
Character Lab
A multi-tenant platform where trainers build and manage their own AI-driven training characters and scenarios, no developer or new build needed.
01
The Brief
Enversed builds a lot of AI-driven training experiences: VR simulations where trainees talk to a virtual client, patient, or colleague. Every one of those AI characters used to live either hardcoded inside a build or scattered across Google Sheets. Changing a single personality trait or adding a new training scenario meant pulling in a developer and shipping a new build. There was no version history, no access control, and no way for a trainer to just go in and tweak something themselves.
Character Lab is the answer to that: a central platform where trainers and content makers configure AI characters, the dialogues they hold, the environments they appear in, and the scenarios that tie it all together, without touching code. Because it had to serve more than one client organization at once, multi-tenancy was a requirement from day one, not something bolted on later.
02
My Approach
Before any of this got built, I did the concepting and research: working out what a character actually needs to be configurable (personality, appearance, knowledge, voice), how those pieces relate to dialogues, environments and scenarios, and how a non-technical trainer should move through all of that without getting lost. I wireframed the configurator, the dashboard and the admin views from that research. A colleague then took those wireframes and turned them into the actual visual design, the mockups I built from.
The biggest architectural decision was to make the whole thing schema-driven. A character, dialogue, scenario or environment each has dozens of fields, and hardcoding a form for every one of them meant every field change would need a frontend deployment. Instead, each entity type gets a single JSON schema defined on the API. That schema is the one source of truth: it drives server-side validation, and the frontend reads it to render the right form fields automatically. Add a field to a schema and it just appears in the UI, no redeploy required.
That schema-driven idea paid off again when I built the configurator, the page where you actually edit a character or scenario. My first version had a separate route and layout per entity type, which meant every new type meant a near-identical new page. I rebuilt it into one generic, dynamic route that reads whatever schema it's given and renders the matching sections and fields, grouped into tabs so a form with dozens of fields doesn't turn into a wall of inputs.
Characters specifically get two variants, Basic and Advanced, and that split came directly out of the research phase: not every trainer wants to sit down and engineer a personality, and not every scenario needs one engineered that deeply. Basic mode keeps a character to the essentials (who they are, what they know, and a Motivation tab built on the DISC model) where you pick the closest of four broad temperaments and move on. Advanced mode replaces that with a proper personality toolkit: a library of twelve narrative archetypes (The Optimist, The Nurturer, The Challenger, and so on) for giving a character a recognizable dramatic shape, full OCEAN Big-5 trait sliders for fine control, explicit primary and secondary stress responses (fight, flight, freeze, fawn) with a live example of how the character would react, and a Language & Speech tab for tone. Switching a character from Basic to Advanced doesn't throw away what was already filled in either: I built a mapping table that pre-fills the OCEAN sliders from the character's DISC profile, so advanced users start from a sensible baseline instead of five blank sliders.
Authentication was something I wanted to get right from the start rather than patch later. Tokens never touch the browser's localStorage or JavaScript at all: they live in httpOnly cookies, injected by a Next.js edge proxy that sits between the frontend and the API. An XSS bug literally cannot read the token, because nothing client-side ever has access to it. That decision made the token-refresh logic more involved, and getting the same setup working behind an nginx reverse proxy on a subpath took a lot of iteration (Swagger docs, redirects and static assets all assume they're running at the root by default), but it was worth the extra complexity for the security guarantee.
Once multiple people started using it at once, a real problem showed up: two trainers editing the same character, and whoever saved last silently overwrote the other's changes. That pushed me into building realtime collaborative editing over Socket.io, with field-level locking so only one person can edit a given field at a time, live indicators showing who's editing what, and an inactivity timer that automatically releases a lock if someone wanders off mid-edit so nothing gets stuck.
A few other features grew out of actually using the thing in practice rather than being planned upfront. Trainers wanted to test how a character actually sounded in conversation before wiring it into a full VR simulation, so I added a chat simulation mode that runs the character through a real conversation via OpenAI. Analytics wasn't in the original scope either, but once real companies were using the API it became clear that visibility into usage, response times and errors was worth having, so I added non-blocking usage logging that writes to Firestore after a response is already sent, so it never slows anything down. And when it turned out some users manage more than one client organization, I added support for switching between companies from a single account, migrating existing user records to the new structure without losing data.
By the end I'd also built out the operational side any real product needs: a role-based access system (super admin, company admin, developer, editor, viewer) and a CLI for migrating existing documents when a schema changes shape.
03
The Result
A working multi-tenant SaaS platform: a Node.js/Express API backed by Firebase, and a Next.js frontend, where a company's trainers and content makers can create and configure AI characters, dialogues, environments and full training scenarios themselves. It includes role-based user and company management, realtime collaborative editing, a chat-based way to test a character before deploying it, and usage analytics for keeping an eye on how the platform is used.
04
Outcome
Character Lab is live and in active use, both internally at Enversed and by client companies who log into it directly to manage their own characters and scenarios, though it isn't a public-facing product on its own; it's the tool feeding characters into Enversed's client-facing VR training builds.
This was also the project that taught me the most about shipping and maintaining an actual product rather than a one-off build. Schema-driven design is a pattern I'll reach for again: it turned "add a field" from a deployment into a data change. Building realtime collaborative editing from scratch was new territory for me. And beyond any single feature, it was my first time carrying a project through the full lifecycle of a real piece of software: versioning, data migrations, production hardening, and all the unglamorous work that comes after the first version ships.
Stills





The characters list for a company. Each one shows its name, a short description, and its assigned voice.
03 / About
Concept first, medium second
I'm Ramon, a multimedia designer based in Eindhoven. Projects start with a problem: what the thing has to do, and how someone should move through it. The answer has been a 360 film, a VR walkthrough, an explainer animation, a configurator and an AI training platform.
My part is 3D, design and code: modelling and lighting in Blender, interface and motion in XD/Illustrator and After Effects, and the build in PlayCanvas or Next.js.