House of Rionysus DGen Studio docs
Contents Writing scenes
  1. Overview
  2. Install
  3. First run and the config
  4. Profiles
  5. The studio
  6. The body
  7. Tools
  8. Keyboard shortcuts
  9. Scenes and presets
  10. Writing scenes
  11. VR
  12. Updates
  13. Reports
  14. Command line
  15. Troubleshooting and FAQ
  16. Licenses and credits

Writing scenes

A scene is one small TypeScript file. It says which body, clip, room and controls the scene has, and how the body reacts when you touch it. You need no build tool: DGen reads the file as it is.

Your scenes folder

Each profile has a scenes folder: scenes in your DGen folder by default. The Scenes tab of the config shows it, and open opens it. DGen writes into it:

FileWhat it is
my-scene.tsAn example scene, written once when the folder is new. Pick My scene in the scene list.
dgen.d.tsThe API's types. DGen writes it again when a new version changes it.
tsconfig.jsonMaps dgen to those types, so your editor autocompletes every builder and value. Written once; yours after.
AGENTS.mdA guide for a coding agent: the same text as the reference below.
CLAUDE.mdOne line that points Claude Code at AGENTS.md. Written once; yours after.

Open the folder in an editor such as VS Code and start from my-scene.ts. Sounds go in a sounds folder beside your scenes.

Let your coding agent write it

Open a coding agent (Claude Code, Codex, Cursor) in your scenes folder and ask for the scene you want, in your own words. AGENTS.md tells it everything: the rules, every builder, the hooks, how to check a file. For example: "a scene where the body smiles when I stroke its arm and looks at my hand when it comes near the face".

Try a scene

  1. Save the file in your scenes folder.
  2. In the studio, pick it in the scene list (reload the page after an edit).
  3. If it fails, the error shows over the scene list with the file and the line.

The scene API

A scene file

The builders of dgen

builderwhat it gives
scenedgen.scene({ title, about, tags, body | bodies, ... }): the scene itself, what the file returns (its keys below)
genesis9dgen.genesis9({ shape, anatomy, character, wear, at }): a Genesis 9 body from the user's Daz library. shape the body's sliders by key ({ size: 0.9, butt: 0.6 }, each in its range), anatomy one of the anatomy ids, character a character's id as the page's character list names it (one the user lacks falls back to plain Genesis 9), wear worn pieces as <item>:<colour> (its hair too), at where it stands, [x, z] or [x, z, heading] (m on the floor: x the room's left, z its front; the heading in deg, 0 facing +z, 90 facing +x), so two bodies on one clip stand apart; a couple's clip places its bodies itself and leaves it out
walldgen.wall('breasts' | 'vulva', { layout, tint }): the region as a wall of copies, its layout one of the region's
bardgen.bar({ x, z, height, radius }): a vertical steel bar on the floor at (x, z) of the body (m; x the body's left, z its front); the body meets it. At most two
lockdgen.lock('hand.L', [x, y, z]): one body of the skeleton held at its joint (a wrist, an ankle...) on the point, a cuff drawn there
torsodgen.torso(): the procedural torso, a body of its own (no clip, room or hooks)
probedgen.probe(): a soft ball on a cone, a body of its own
lettersdgen.letters({ look }): a word as soft flesh, its surface
skindgen.skin(): a body's own skin, as a surface
surfacedgen.surface('latex', { tint: [r, g, b] }): one of our materials in a colour (linear rgb, 0 to 1); dgen.surface('<item>:<colour>', { material, tint }): one material of a wardrobe colour
choicedgen.choice(id, ['a', 'b']) or dgen.choice(id, { a: valueA, b: valueB }): a control the user picks on the page, put where a value goes (its first option the default, def another)
objectdgen.object('sphere' | 'cube' | 'cone', { size, at: [x, y, z] }), or { size, path: [{ at, t }, ...] } to move it on the clip's time (s): the page's one object
sounddgen.sound('file.ogg', { volume }): a sound file of sounds/ in this folder (mp3, ogg, opus, wav, flac, m4a), volume 0 to 1
linedgen.line('file.ogg', { volume }): a spoken line, a sound of sounds/ with its lips track file.lips beside it (one row a phoneme: <start s> <end s> <viseme> <phoneme> <word>)
ondgen.on(event, filter?, (e, scene) => ...): a hook, its function run in the page when the event matches the filter (below)

The keys of dgen.scene

keywhat it is
titleits name in the scene list
aboutone line of what it is, 120 characters at most (the types ask for it; a scene without it still opens, its tile saying "no description"): the scene list shows it on hover, its filter matches it
tagsthe words that sort and find it, one at least (the types ask for them, as for about):physics (its flesh, skeleton or object is the point: it moves, swings, dents), interactive (its hooks answer a touch, a grab, a pointer near), couple (two bodies or more), wall (a region as a wall of copies), demo (a body that is not Genesis 9: a test of the solve), speech (a body speaks, its lips in sync), clothed (a body wears clothes), male (a masculine body), adult (adult content: nudity, genitals, a sexual act)
bodythe body: dgen.genesis9(), or torso, probe, letters
bodiestwo Genesis 9 bodies or more, in place of body (no wall or bars then)
clipthe motion clip it plays, by its name in the page's clip list; one the user lacks plays their first clip, named over the stage. A couple's clip plays its part i on body i; none plays no clip: each body holds its pose, its flesh live
roomthe room: studio, dark, sky, plain
camerathe camera's home: { yaw, pitch, dist, y } (rad, rad, m, m: the height it looks at)
walldgen.wall(...), on one Genesis 9 body
barsat most two dgen.bar(...)
locksdgen.lock(...) each
objectdgen.object(...)
hooksdgen.on(...) each
musica dgen.sound(...) looped from the first frame
assetsthe Daz products it uses beyond Genesis 9 Starter Essentials, by id, each 'mandatory' (the scene does not open without it, and says so) or 'optional' (it runs, that piece off): { 'expression-fusion': 'optional' }. The page lists the ones the user's library lacks under the scene's name, with where to get them. The ids: golden-palace (Golden Palace for Genesis 9: the female anatomy, its graft and its dials), dicktator (Dicktator for Genesis 9: the male anatomy, its graft and its dials), alicia (DEX Alicia 9: a character, its shape and skin), emphasized-visemes (Emphasized Visemes for Genesis 9: the mouth on a spoken line), breast-control (Breast Control for Genesis 9: the breast shape dials), expression-fusion (Expression Fusion: the face as it moves), eirgrid (Eirgrid Hair for Genesis 9: the default hair), chloe (CS Chloe Hair for Genesis 9: a second hair)

Only title and body (or bodies) are needed. Clip, room, object, hooks and music are for Genesis 9 bodies.

Controls

A control is a value the user picks on the page: dgen.choice(id, options) put where the value goes, for example look: dgen.choice('surface', { flesh: dgen.skin(), latex: dgen.surface('latex', { tint: [0.6, 0, 0.1] }) }) on dgen.letters, or layout: dgen.choice('layout', ['anemone', 'spiral']) on a wall. The page draws each control the scene uses, under the scene's name, and keeps the pick in the url under its id. An id must be unique in the scene.

Hooks

dgen.on(event, filter, (e, scene) => ...). A touch event's filter may be left out (it then takes any touch).

eventfires when
touchanything starts touching on
pressa touch by the Press tool (or a VR finger) starts
graba touch by the Grab tool starts
pincha touch by the Pinch tool starts
strokea press slides 4 cm along the skin since its touch or its last stroke
releasethe touch ends (let go, lifted, moved off); e.held how long (s), e.hard its most
nearby comes within within m of on
awayit goes past 1.25 times within again
afterseconds after the scene opens, once
everyeach seconds, the first at seconds

What a hook's function acts on, scene (the selected body):

actiondoes
emotionscene.emotion('smile', { amount, ease, for }): one face to amount (1), the others to 0, eased over ease s (0.3); with for, back after that many s
sliderscene.slider('eyesClosed', 1, { ease }): any slider of the body's shape, eased
gazescene.gaze('camera' | 'off' | e.by | [x, y, z]): the eyes follow it (a pointer or a hand as it moves)
musicscene.music(sound, { fade }): the one music, looped, crossfaded over fade s (1); null fades it out
playscene.play(sound): a sound once, over the music
sayscene.say(line): the body says a dgen.line(...): its sound once and its mouth on the line's lips, in time
clipscene.clip('name'): another clip from its start; scene.clip('none') stops the clip, each body holding the pose it has
afterscene.after(seconds, (scene) => ...): run later, on hook time
timescene.time: hook time, s since the scene opened

A hook that throws shows its event and its message over the stage for 6 s; the other hooks go on.

What throws

The builders, or the page as it opens the scene, throw naming the value and the valid ones on: a slider out of its range, a worn piece that does not read, a place that is not two or three numbers, an unknown event, region, by or tool, a near hook with no within, a time hook with no seconds, an object size out of its range, a sound that is not a file of sounds/ or whose volume is not 0 to 1, a third bar, a control with no option or a def that is not one of them, an asset id that is not one of the ids or a need that is not mandatory or optional, an about empty, of two lines or past 120 characters, no tag or a tag that is not one of them. The page shows the error over the scene list.

Check a scene

  1. Types: this folder has a tsconfig.json. bun check here (Bun 1.4 or later), or npx tsc -p ., names each wrong value with its file and line, and nothing when every scene compiles.
  2. The server: curl -s http://localhost:<port>/api/scenes/<name>.js (the address DGen opened) answers the scene as JavaScript, or why it is refused (an import, a syntax error and its line).
  3. The page: pick the scene in the scene list at the top. Reload the page after an edit.

Every body is an adult

A character whose name or product says young, teen, child or kid is refused, whatever its pack. Never write a scene that makes a body look young.

The example (my-scene.ts, written when the folder was new)

import type { Dgen } from 'dgen'

// a scene: one file of this folder, its name the scene's (lower case letters, digits and dashes). It imports nothing
// but the type of dgen; open the folder in an editor for its autocompletion. Pick it in the scene list at the top.
// Its hooks run when the body is touched; its sounds are files of sounds/ beside it: dgen.sound('my-music.mp3')
export default (dgen: Dgen) =>
    dgen.scene({
        title: 'My scene',
        // what it is, for the scene list: one line, and the words that sort and find it
        about: 'A body that smiles when stroked and is surprised when pinched',
        tags: ['interactive', 'adult'],
        body: dgen.genesis9({ shape: { size: 0.8, butt: 0.6 } }),
        // the Daz products it uses beyond Genesis 9 Starter Essentials: an optional one it runs without (the grin
        // below then keeps the face still), a mandatory one it does not open without; the page lists the ones your
        // library lacks, with where to get them
        assets: { 'expression-fusion': 'optional' },
        hooks: [
            // a smile for three seconds when the body is stroked (the Press tool, dragged along the skin)
            dgen.on('stroke', (_, scene) => scene.emotion('smile', { for: 3 })),
            // surprised at a pinch
            dgen.on('pinch', (_, scene) => scene.emotion('surprised', { for: 1.5 })),
            // a grin at a grab, a face of Expression Fusion
            dgen.on('grab', (_, scene) => scene.emotion('ef10', { for: 2 })),
        ],
    })