A minimalist Visual Novel / Interactive Fiction engine built with Moonbit and WebAssembly.
This project implements a Visual Novel engine where:
- Core logic is written in Moonbit and compiled to WebAssembly (Wasm)
- UI is built with vanilla HTML/CSS/JavaScript
- Story data is defined in JSON format following a defined schema
moonslg/
├── moon.mod.json # Moonbit module configuration
├── moon.pkg.json # Moonbit package configuration with Wasm exports
├── main.mbt # Core Moonbit source code (data structures & logic)
├── index.html # Main HTML entry point
├── css/
│ └── style.css # Visual novel UI styles
├── js/
│ ├── wasm-loader.js # WebAssembly module loader
│ └── app.js # Main application logic
├── schema/
│ └── story.schema.json # JSON schema for story data
├── data/
│ └── example-story.json # Example story data file
└── target/ # Build output directory (generated)
└── wasm-gc/
└── vn-engine.wasm # Compiled WebAssembly module
Represents a player-selectable option in the story.
pubstructChoice {
mut text: String// The display text for this choice
mut target_node_id: Int// The ID of the node this choice leads to
}Represents a single story node/scene in the visual novel.
pubstructNode {
mut id: Int// Unique identifier for this node
mut background_image_url: String// URL or path to the background image
mut text_content: String// The main text content displayed
mut choices: Array[Choice] // Available choices for the player
}Manages the current state of the visual novel.
pubstructStoryState {
mut nodes: Map[Int, Node] // All story nodes indexed by ID
mut current_node: Option[Node] // Currently active node
mut selected_choice: Int// Currently selected choice index
}The following functions are exported from the Wasm module:
| Function | Description |
|---|---|
get_current_node() | Returns the current story node |
get_node_id(node) | Gets the ID of a node |
get_node_background_url(node) | Gets the background image URL |
get_node_text_content(node) | Gets the text content |
get_choices_count(node) | Returns the number of choices |
get_choice_text(node, index) | Gets choice text at index |
get_choice_target_id(node, index) | Gets target node ID for choice |
load_story_data(json_string) | Loads story from JSON |
select_choice(index) | Selects a choice and navigates |
Story data follows the JSON schema defined in schema/story.schema.json. The schema maps directly to the Moonbit structs:
{
"metadata": {
"title": "Story Title",
"author": "Author Name",
"version": "1.0.0"
},
"start_node_id": 1,
"nodes": [
{
"id": 1,
"background_image_url": "images/scene1.png",
"text_content": "Your story text here...",
"choices": [
{
"text": "Choice text",
"target_node_id": 2
}
]
}
]
}- Moonbit installed on your system
- A modern web browser with WebAssembly support
# Build the project for Wasm target
moon build --target wasm-gc
# The output will be at:# target/wasm-gc/vn-engine.wasmTo run the project locally, you need a web server (required for Wasm loading):
# Using Python 3
python -m http.server 8080
# Using Node.js (npx)
npx serve .# Using PHP
php -S localhost:8080Then open http://localhost:8080 in your browser.
- Build the Moonbit project to generate the Wasm module
- Start a local web server
- Open
index.htmlin a browser - The engine will automatically load and display the demo story
To load a custom story:
- Create a JSON file following the schema in
schema/story.schema.json - Place the file in the
data/directory - Modify
js/app.jsto load your story file instead of the demo
┌─────────────────────────────────────────────────────────┐
│ Browser │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ index.html │───▶│ app.js │───▶│ wasm-loader │ │
│ └─────────────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ style.css │ │ vn-engine │ │
│ └─────────────┘ │ .wasm │ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
This is Phase 1 of the project, which includes:
- ✅ Moonbit project structure with Wasm target configuration
- ✅ Core data structures (
Node,Choice,StoryState) - ✅ HTML/CSS/JS boilerplate for Wasm loading
- ✅ JSON schema for story data
- ✅ Example story data
- ✅ Basic demo story hardcoded in Moonbit
- JSON parsing is not yet implemented in Moonbit; story data is hardcoded
- String handling between Wasm and JS may need adjustment based on Moonbit's actual memory layout
- No save/load functionality yet
- Phase 2: JSON parsing and external story loading
- Phase 3: Save/Load game state
- Phase 4: Audio support (BGM, sound effects)
- Phase 5: Character sprites and animations
- Phase 6: Visual editor for story creation
MIT License
Contributions are welcome! Please feel free to submit issues and pull requests.