KrulVN2 - KrulTsepeshi

KrulVN 2 Documentation

KrulVN2 Documentation

KrulVN is a Luau visual novel engine for Roblox. Developers use it to create unique visual novel stories without the worry of implementing every feature.

Common entrypoint
local KrulVN = require(script.KrulVN)
local Story = KrulVN.new(storyName:string)

Usage

Want more examples? Please contact and request me to add more, I will gladly expand usage tutorials.
UI
How to modify the look of UI

You can modify the UI in any way you want, as long as you keep the structure the same. You can always add new UI objects!

Settings
How to modify default client settings?

You can modify default client settings in the ClientSettings module found under the StarterGui part of KrulVN!

How to modify default server settings?

You can modify default server settings in the ServerSettings module found under the ServerScriptService part of KrulVN!

Scripts
How to create characters

Creating characters is very straightforward.

To get started, create a variable that will hold your character. See here!

Characters

Story:newCharacter(name: string | {variable:string,identifier:string} | "_narrator"..., textColor: Color3, nameColor: Color3)

Create a new character for dialogue. If name starts with "_narrator" it will automatically have narrator mode enabled. All character names must be unique

Narrator mode hides the name box.

  • name (string, required)
  • textColor (Color3, required)
  • nameColor (Color3, required)
local Reze = Story:newCharacter("Reze", Color3.new(0.8,0.5,1), Color3.new(1,1,1))
Story:getCharacter(name: string): Character | nil

Retrieve a character by name.

Story:removeCharacter(name: string)

Remove a character from the story.

Dialogue

Story:say(character: Character, text: string, options?: sayOptions)

Display dialogue from a character or narrator.

  • character (Character, pass _narrator for narrator lines)
  • text (string, required)
  • options (sayOptions, optional)
Story:say(Reze, "Hello!", { customTextSpeed = 0.1 })

Markdown formatting

The text field supports Markdown and custom brace tokens for rich formatting:

  • **bold**
  • *italic*
  • __underline__
  • ~~strikethrough~~

Brace tokens allow advanced styling:

  • {size=32:Large text}
  • {color=#ff0000:Red text}
  • {stroke=color="#0000ff" thickness="2":Outlined text}
  • {mark=#ffff00:Highlighted text}
  • {uppercase:Shouting}
  • {smallcaps:Stylized}

-- Examples:
Story:say(Reze, "This is **bold** and *italic* text.")
Story:say(Reze, "{color=#00ff00:Green words} mixed with {size=44:big letters}.")
Story:say(Reze, "{stroke=color=\"#ff0000\" thickness=\"3\":{size=32:Important!}}")
  

Preview

InputRendered Output
**bold**bold
*italic*italic
__underline__underline
~~strikethrough~~strikethrough
{size=32:Large text}Large text
{color=#ff0000:Red text}Red text
{stroke=color="#0000ff" thickness="2":Outlined text}Outlined text
{mark=#ffff00:Highlighted text}Highlighted text
{uppercase:Shouting}SHOUTING
{smallcaps:Stylized}Stylized
**Bold** and {color=#00ff00:green} with {size=28:big letters}Bold and green with big letters
Story:addText(text: string, options?: sayOptions)

Append text to the current dialogue box.

Flow Control

Story:wait(seconds: number)

Pause progression for a set time.

Story:addLabel(name: string)

Create a label to jump to later.

Story:jumpToLabel(name: string)

Jump to a previously defined label.

Story:insertChoice(prompt: string, choices: {Choice})

Present interactive choices. Each choice must have text and nextLabel.

Story:insertInput(prompt: string, inputSettings: Types.InputSettings, targetVariable:string)

Present an input field. Input settings must have minLetters.

targetVariable will be the variable that the input gets set to.

Variables

Story:defineVariable(name: string, defaultValue: string|number|boolean|nil)

Define a variable for branching, text replacement or such.

Story:editVariable(name: string, operator: string, properties: generalProperties)

Modify a variable using arithmetic. properties.type can be "value" or "variable".

Story:printVariable(name: string)

Print a variable’s value (debugging).

Story:jumpIf(variable: string, comparator: string, value: any, label: string)

Conditional branching. Comparators: ==, ~= (or !=), >, <, >=, <=.

Additional comparators:

  • equal | equal is basically "==" but non case sensitive and meant for strings.
  • !equal | !equal is basically ~= but non case sensitive and meant for strings.

Assets

Story:setBackground(name: string)

Change background.

Story:show(name: string, assetName: string, position: krulUDim2, options?: showOptions)

Show an asset (character sprite, prop, etc.). Name can not be "*".

Story:hide(name: string, options?: hideOptions)

Hide an asset (character sprite, prop, etc.). Hiding "*" will hide all shown assets.

Story:pop(name: string, options: PopData, ignoreMissing?: boolean)

Remove or animate an asset.

Audio & Effects

Story:setMusic(name: string, looped: boolean, play: boolean)

Play background music.

Story:playSFX(name: string, delay: number)

Play a sound effect. Delay must be between 0 and 5.

Story:darkenScreen(to: number, layer: EffectsLayer, animation: darkenScreenAnimation)

Apply a screen darkening effect.

Settings

Story:enable({ name: "allowSaving"|"allowSkipping"|"allowAuto" })

Enable player features.

Story:disable({ name: "allowSaving"|"allowSkipping"|"allowAuto" })

Disable player features.

Markdown Best Practices

Keep formatting simple

Use Markdown sparingly to emphasize important words or phrases. Overusing bold, italic, or colors can make dialogue harder to read.

Combine styles thoughtfully

You can mix inline Markdown with brace tokens (e.g. **Bold** and {color=#00ff00:green}), but keep combinations clear and avoid nesting too deeply.

Use nesting for impact

Nesting brace tokens (e.g. {stroke=color="#ff0000" thickness="2":{size=32:Warning!}}) is powerful, but should be reserved for special emphasis like warnings or dramatic dialogue.

Maintain readability

Choose sizes and colors that remain legible. Extremely large sizes or low-contrast colors may reduce accessibility.

Highlight sparingly

Use {mark=#ffff00:highlight} for key words or phrases, not entire sentences. This keeps the reader’s focus sharp.

Consistent style across characters

Apply formatting consistently for each character. For example, one character might always speak in {smallcaps:small caps} to distinguish their voice.

Test combined effects

Preview your dialogue with multiple styles applied to ensure the final rendering looks as intended and doesn’t clash visually.

Do & Don’t Examples

DoDon’t
Use **bold** for emphasis on key words.Make entire sentences bold — it reduces readability.
Mix inline Markdown with one brace token (e.g. **Bold** and {color=#00ff00:green}).Nest multiple brace tokens deeply (e.g. {size=32:{stroke=…:{mark=…}}}).
Choose high‑contrast colors like {color=#ff0000:Red}.Use low‑contrast colors (e.g. yellow text on white background).
Apply {mark=#ffff00:highlight} to single words or phrases.Highlight entire paragraphs — it overwhelms the reader.
Keep sizes moderate (e.g. {size=28:Big}).Use extreme sizes (e.g. {size=200:HUGE}) that break layout.
Give each character a consistent style (e.g. one always speaks in {smallcaps:small caps}).Change formatting randomly between lines for the same character.

Type References

Character

Represents a character defined with newCharacter.

export type Character = {
  name:string|{variable:string,identifier:string},
  textColor:krulColor3,
  nameColor:krulColor3
}
generalProperties

Used in editVariable to specify whether you’re editing with a literal value or another variable.

export type generalProperties = {
  type: "variable" | "value",
  value: string | number | boolean | nil,
}
sayOptions

Optional settings for say and addText.

export type sayOptions = {
  customTextSpeed?: number,
  overrideTextColor?: krulColor3,
  async?: boolean,
  voicelineName?: string,
  playVoicelineEachLetter?: boolean,
  clickToFillState?: boolean,
  skipResume?: number | boolean,
  disableSkipWhitespace?: boolean,
  replace?: { pattern: string, replaceWith: generalProperties }[],
}
EnableAndDisable

Used in enable and disable to toggle player features.

export type EnableAndDisable = {
  name: "allowSaving" | "allowSkipping" | "allowAuto",
}
movementAnimation

Used in showOptions.animation to animate asset movement.

export type movementAnimation = {
  direction: Enum.EasingDirection,
  style: Enum.EasingStyle,
  AppliedX: krulUDim,
  AppliedY: krulUDim,
  Time: number,
}
EffectsLayer

Numeric layer used in darkenScreen to control render order.

export type EffectsLayer = number
PopData

Controls how an asset is popped in/out.

export type PopData = {
  size: { xs: number, xo: number, ys: number, yo: number },
  pos:  { xs: number, xo: number, ys: number, yo: number },
  async: boolean,
}
darkenScreenAnimation

Animation settings for darkenScreen.

export type darkenScreenAnimation = {
  direction: Enum.EasingDirection,
  style: Enum.EasingStyle,
  Time: number,
}
showOptions

Optional settings for show.

export type showOptions = {
  customizedSize?: krulUDim2,
  animation?: movementAnimation,
  async?: boolean,
  anchor?: krulUDim,
  customZIndex?: number,
  animationSkippable?: boolean,
}
hideOptions

Optional settings for hide.

export type hideOptions = {
  animation:movementAnimation | nil,
  async:boolean | nil,
  animationSkippable:boolean | nil
}
Label

Represents a label target for jumps.

export type Label = {
  name: string,
  spot: number,
}
Choice

Used in insertChoice.

export type Choice = {
  text: string,
  nextLabel: string,
}
Input

Used in insertInput.

export type InputSettings = {
  minLetters:number,
  maxLetters:number | nil
}
Different types to replace UDim and such

export type krulVector3 = {X:number,Y:number,Z:number}
export type krulVector2 = {X:number,Y:number}
export type krulColor3 = {v1:number,v2:number,v3:number}
export type krulUDim = {Scale:number,Offset:number}
export type krulUDim2 = {X:{Scale:number,Offset:number},Y:{Scale:number,Offset:number}}
                
StoryPiece

Represents a queued story action.

export type StoryPiece = {
  character?: Character,
  text?: string,
  options?: sayOptions,
  type?: "say" | "text" | "choice",
  choices?: Choice[],
}