This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
webTaleKit is a TypeScript-based visual novel game engine that allows creating interactive stories using HTML, CSS, and JavaScript. The engine supports flexible UI creation, scenario control with markup language and JavaScript, and automatic scaling for various window sizes.
npm run build- Compile TypeScript to JavaScript and prepare distribution filesnpm run dev- Build the project and run development server in example foldernpm run lint- Run ESLint for code quality checkingnpm run test- Run Jest testsnpx jest path/to/file.test.ts- Run a single test filenpm run docs:dev- Start VitePress documentation dev servernpm run docs:build- Build documentation site
wtc- WebTaleScript parser CLI (available viaparser/cli.js)- Usage:
wtc <scene-file> [output-directory]to convert .scene files to .js/.ts files
- Core (
src/core/index.js): Main 976-line command dispatcher and state orchestrator. HoldsdisplayedImages(currently shown images),sceneFile(loaded scene exports), andsceneConfig. Note: this file is plain JavaScript, not TypeScript. - ScenarioManager (
src/core/scenarioManager.ts): Tracks execution index, injects dynamic choice branches (markedsub=true), manages text history/backlog. - Drawer (
src/core/drawer.ts): Canvas-based rendering — text animation, choice buttons, image compositing, fade transitions, responsive scaling. - ResourceManager (
src/core/resourceManager.ts): Lightweight asset registry (currently underdeveloped; actual loading happens inline in command handlers).
core.start(initScene)
→ loadScene() // dynamic import of compiled .scene JS file
→ loadScreen() // inject HTML template + create canvas
→ register input events (Enter / Ctrl / Click)
→ loop: while scenarioManager.hasNext()
→ runScenario()
→ scenarioManager.next() // get command object
→ evaluate if-attribute (conditional skip)
→ await commandList[type](commandObject)
Command handlers are methods on Core, dispatched by commandList[type]:
| Command | Notes |
|---|---|
text |
Character-by-character animation; mustache variable expansion |
say |
Dialogue = voice playback + textHandler |
choice |
Injects selected branch into scenario with sub=true |
show/hide |
Canvas image compositing; modes: bg, chara, cg, cutin, effect |
jump |
Index-based jump within same scene; cleans up sub-branches on backward jump |
route |
Scene transition — stops BGM, clears display, loads new scene file |
if |
Evaluates JS expression; injects matching branch into scenario |
call |
Executes arbitrary JS with sceneFile exports in scope |
sound |
BGM/SE/voice; modes: play, stop, pause |
wait |
Timed pause; respects auto-advance mode |
moveto |
Animated image position change |
newpage |
Clears text + resets displayedImages to background only |
When a choice is selected, choiceHandler inserts the chosen branch into the scenario array at the current index, with every inserted item marked sub: true. This is a temporary injection — not part of the original scenario.
jumpHandler must clean up these sub-items on backward jumps:
- Keep
beforeslice (pre-jump-destination) - Filter
slice(jumpDest, currentIndex)to removesub=trueitems - Filter
slice(currentIndex)(future) to removesub=trueitems - Reconstruct and re-set the scenario via
scenarioManager.setScenario()
Without this cleanup, re-entering a choice repeatedly stacks orphaned sub-branches. jump only works within the same scene; use route for scene transitions.
.scene files are HTML-like markup parsed by the CLI into JavaScript. The parser extracts <scenario> content into a command array and the <script> block into JS exports.
<scene>
<scenario>
<show mode="bg" src="./bg.png"/>
<say name="Alice">Hello!</say>
<choice>
<item label="Option A"><jump index="10"/></item>
<item label="Option B"><jump index="20"/></item>
</choice>
</scenario>
<script type="text/typescript">
export const sceneConfig = {
name: 'scene-name',
background: './path/to/bg.png',
bgm: './path/to/music.mp3',
template: './path/to/ui.html'
}
export let counter = 0; // accessible via {{counter}} in scenario
</script>
</scene>Variables exported from <script> are available via mustache syntax ({{variable}}) and are in scope for <call> and <if> expressions. Evaluation uses new Function(...keys, code) with sceneFile exports as context.
All commands support conditional execution with an if attribute — the command is skipped if the expression evaluates to false.
engineConfig.json- Main engine configuration (title, resolution, etc.)example/src/resource/config.js- Resource definitions for games- Template HTML files define UI layouts (referenced via
sceneConfig.template)
- Uses Jest with
ts-jest; test files use.test.tsextension - E2E tests in
tests/e2e/using Playwright - Coverage reports in
coverage/
- Compiles TypeScript files to JavaScript
- Copies
package.json,README.md,engineConfig.json,parser/ - Outputs to
dist/;src/core/index.jsis the main entry point