Skip to content

Redmega/simple-visual-novel

Repository files navigation

Simple Visual Novel Engine

A minimal TypeScript visual novel engine with vanilla DOM rendering, supporting dialogue, character visibility, and scene transitions.

Dependent repos (via libraries.io) Read the Docs

Docs

The docs can be found at https://simple-visual-novel.readthedocs.io/en/latest

Features

  • Class-based Script API - Build stories using a fluent, type-safe API
  • Dialogue System - Per-dialogue effects (fade, typewriter)
  • Character Management - Show/hide characters dynamically, change images mid-scene
  • Scene Navigation - Automatic progression through scenes
  • Game State - Variable management for flags and counters
  • Event System - Listen to scene changes and variable updates

Installation

npm (for ES modules)

npm install simple-visual-novel

Browser Bundle

A browser bundle is available via unpkg:

https://unpkg.com/simple-visual-novel@latest

Of a minified version:

https://unpkg.com/simple-visual-novel@latest/dist/simple-visual-novel.min.js

Or build it yourself:

npm install
npm run build
# The bundle will be in dist/simple-visual-novel.js

Development

# Build TypeScript
npm run build

# Watch mode
npm run dev

# Run tests
npm test

Usage

Browser (Script Tag)

Include the bundled version in your HTML. The bundle is available in dist/simple-visual-novel.js after building:

<!DOCTYPE html>
<html>
<head>
  <title>My Visual Novel</title>
</head>
<body>
  <div id="game-container"></div>
  
  <!-- Include the bundled library -->
  <script src="path/to/simple-visual-novel.js"></script>
  
  <script>
    // Access the library via the SimpleVN global
    const { Script, Scene, Character, VNEngine } = SimpleVN;
    
    // Create your story
    const script = new Script();
    const narrator = new Character("Narrator");
    const scene1 = new Scene("scene1", { background: "park.png" });
    scene1.add(narrator);
    narrator.say("Hello, world!");
    script.addScene(scene1);
    
    // Initialize the engine
    const engine = new VNEngine({
      script: script,
      container: "#game-container",
      startScene: "scene1",
      renderer: {
        assetsDirectory: "assets", // Optional: base directory for asset paths
        // With assetsDirectory set, "park.jpg" becomes "assets/park.jpg"
      },
    });
  </script>
</body>
</html>

Note: After installing via npm, the browser bundle will be in node_modules/simple-visual-novel/dist/simple-visual-novel.js. You can copy it to your project or use a bundler.

ES Modules

import { Script, Scene, Character, VNEngine } from "simple-visual-novel";

// Create script
const script = new Script();

const narrator = new Character("Narrator");
const protagonist = new Character("Protagonist", "protagonist.png"); // Character with sprite image

const scene1 = new Scene("scene1", { background: "park.png" });
scene1.add(narrator); // narrator is shown automatically
narrator.say("It was a sunny day.", { effect: "fade" });
narrator.say("The birds were chirping.");
scene1.add(protagonist); // protagonist is shown automatically
protagonist.say("I wonder what will happen?", { effect: "typewriter" });
script.addScene(scene1);

// Initialize engine (renderer is created automatically)
const engine = new VNEngine({
  script: script,
  container: "#game-container", // or a DOM element
  startScene: "scene1",
  renderer: {
    typewriterSpeed: 50, // characters per second
    assetsDirectory: "assets", // Optional: base directory for asset paths
  },
});

Story Script Format

Scenes progress automatically in the order they are added to the script. No explicit nextScene is needed.

const script = new Script();

const narrator = new Character("Narrator");
const protagonist = new Character("Protagonist");

const scene1 = new Scene("scene1", { background: "park.png" });
scene1.add(narrator);
narrator.say("It was a sunny day.");
narrator.say("The birds were chirping.");
scene1.add(protagonist);
protagonist.say("I wonder what will happen?");
narrator.hide();
script.addScene(scene1);

const scene2 = new Scene("scene2", { background: "cafe.png" });
scene2.add(protagonist);
protagonist.say("Let's go to the cafe!");
script.addScene(scene2);
// scene2 automatically follows scene1

Dialogue Effects

Dialogue can have optional effects:

  • { effect: "typewriter" } - Character-by-character animation
  • { effect: "fade" } - Fade in animation
  • No effect - Display immediately

Changing Character Images

Characters can have their images changed dynamically during a scene by setting the image property. This is useful for showing different character expressions or outfits:

const character = new Character("Alice", "alice-neutral.png");

scene.add(character);
character.say("Hello!");
character.image = "alice-happy.png"; // Change to happy expression
character.say("I'm so happy to see you!");
character.image = "alice-surprised.png"; // Change to surprised expression
character.say("Wait, what's that?!");

When the character is in a scene, setting image automatically queues an action to update the displayed sprite. If the character is not yet in a scene, it just updates the internal state.

Character Positioning and Sizing

Characters can be positioned and sized using various methods:

Position Types

  • Named positions: "left", "center", "right", "far-left", "far-right"
  • Normalized coordinates (0.0-1.0): { x: 0.5, y: 1.0 } - Converted to percentages
  • Pixel values: { x: "100px", y: "50px" }
  • Percentage strings: { x: "30%", y: "75%" }

Size Types

  • Normalized values (0.0-1.0): { width: 0.3, height: 0.6 } - Converted to percentages
  • Pixel values: { width: "300px", height: "400px" }
  • Percentage strings: { width: "50%", height: "60%" }

Usage Examples

const character = new Character("Alice", "alex.png");

// Method 1: Using getters/setters
character.position = "left";
character.size = { width: "300px", height: "400px" };
character.show();

// Method 2: When adding to scene
scene.add(character, { position: "left", size: { width: "300px" } });

// Method 3: Normalized coordinates (0.0-1.0)
character.position = { x: 0.3, y: 0.8 };
character.size = { width: 0.3, height: 0.6 };
scene.add(character, { position: { x: 0.7, y: 1.0 }, size: { width: 0.4, height: 0.7 } });

// Method 4: Pixel values
character.position = { x: "100px", y: "50px" };
character.size = { width: "200px", height: "300px" };
scene.add(character, { position: { x: "200px", y: "0" }, size: { width: "250px" } });

// Method 5: Mixed (percentage strings)
character.position = { x: "30%", y: "80%" };
character.size = { width: "50%", height: "60%" };

// Method 6: Override in show() or scene.add()
character.position = "center";
character.show({ position: "left", size: { width: "400px" } }); // Overrides position to left, size to 400px width

Position Coordinate System

  • X-axis: 0.0 = left edge, 0.5 = center, 1.0 = right edge
  • Y-axis: 0.0 = top edge, 1.0 = bottom edge (characters are typically bottom-aligned)
  • Characters are positioned using CSS left and bottom properties
  • Percentage-based x positioning uses transform: translateX(-50%) for center alignment
  • Default position when not specified: center-bottom

Named Position Mappings

  • "far-left": x = 10% from left
  • "left": x = 25% from left
  • "center": x = 50% from left (centered)
  • "right": x = 75% from left
  • "far-right": x = 90% from left
  • All named positions default to y = 0 (bottom-aligned)

HTML Structure

The renderer creates the following DOM structure:

<div id="game-container">
  <div class="vn-background-layer"></div>
  <div class="vn-character-layer">
    <div class="vn-character" data-character-name="..."></div>
  </div>
  <div class="vn-dialogue-box">
    <div class="vn-speaker-name"></div>
    <div class="vn-dialogue-text"></div>
  </div>
</div>

Project Structure

simple-visual-novel/
├── src/
│   ├── core/
│   │   ├── engine.ts          # Main engine class
│   │   ├── types.ts           # TypeScript interfaces and classes
│   │   └── state.ts           # Game state management
│   ├── renderer/
│   │   ├── renderer.ts        # DOM rendering logic
│   │   └── effects.ts         # Text effects (typewriter, fade)
│   ├── examples/
│   │   └── exampleNovel.ts    # Example story script
│   └── index.ts               # Entry point
├── tests/                      # Test files
├── example/
│   ├── index.html             # Main HTML file
│   └── styles.css             # Styling
└── package.json

License

MIT

About

A Simple Visual Novel engine, in the browser

Resources

License

Stars

Watchers

Forks

Contributors 2

  •  
  •