LVNAuth Tutorials - Web-connected Visual Novels

Navigate this article

Introduction
What is a web-connected visual novel?
Connectivity
Programming languages
Requirements
Using LVNAuthWeb
Remote commands
Custom Python code
Passing arguments to LVNAuthWeb
Generating LVNAuth scripts with PL/pgSQL
Save a value
Get a value
Get a value into a variable
'remote_get' with a variable VS without a variable
Where the saved data is stored
Shared license key VS Private license key

Introduction

Starting with LVNAuth version 0.8, it's possible to create visual novels that can retrieve an LVNAuth script from a web server. This opens up possibilities for implementing more complex logic than what's possible within an offline LVNAuth script.

On your web server you could utilize Python and/or PL/pgSQL (PostgreSQL) procedures to come up with different ways for your visual novel's story to branch off.

A web-enabled visual novel does not mean that a visual novel will run in a web browser. It still runs on a desktop computer. Web-enabled in LVNAuth means that your visual novel can store and retrieve data from your own remote (or local) server.

Web servers for LVNAuth are maintained by you (the visual novel author). The software that gets used on the server to connect to LVNAuth visual novels is called LVNAuthWeb.

Things you can do with web-connected visual novels

Here are some of the things you can do with a web-connected visual novel in LVNAuth:

  1. Have a character in your visual novel show the latest news or weather info by obtaining the latest news data from the internet.

  2. Characters that have AI responses.

  3. Save or load your viewers' choices from your visual novel in a database. That way, when your viewer plays your visual novel later on, the choices will be remembered, regardless of which computer the visual novel is playing from, because the data will be stored remotely on your own server.

  4. Digital Rights Management (DRM). A DRM system is built-in which allows you to allow visual novels to be played only for paying viewers. Payments received via Ko-fi can be detected in LVNAuthWeb, when configured correctly. This can be used to determine whether a visual novel has been paid for. For paid visual novels, LVNAuthWeb expects visual novels to require a monthly payment for it to be viewable. Each viewer gets a license key and each license key is valid for 1 package. Each package can contain 1 or more visual novels.

Connectivity

Your visual novel can optionally be mostly offline except for a few online remote web calls or you can make almost the entire visual novel get/save data using an LVNAuthWeb server. The choice is yours. However, if your visual novel is web-enabled (i.e has the ability to make request data from LVNAuthWeb), your visual novel will need to be able to connect to your LVNAuthWeb server; otherwise your visual novel won't play.

In other words, if your LVNAuthWeb server is down or is not reachable, your visual novel won't play, because LVNAuth checks to see if it can reach the server first before playing the visual novel.

Programming languages

Web-connected visual novels utilize Python and PL/pgSQL on a self-hosted server. Almost your entire visual novel (or just parts of it) can be written in Python or PL/pgSQL, as long as it generates and returns an LVNAuth script.

Requirements

Server

Using LVNAuthWeb

LVNAuthWeb doesn't come in binary format; just source code files. 1. Download LVNAuthWeb

  1. Install PostgreSQL if you haven't already done so.

  2. Run the sql script in PostgreSQL to create the database and stored procedures that LVNAuthWeb needs to run.

  3. Run the Python script: db_shell.py. This is the starting point for doing the following things:

  4. Configuring the connection from LVNAuthWeb to the PostgreSQL database and
  5. Configuring the web server by setting the listening address and port number.
  6. Adding visual novels to the database
  7. Adding packages to the databases. A package holds visual novels. One package can consist of 1 or more visual novels.

  8. Once all the configurations are done from step 4, then start the web server by running the Python script: web_start.py. This script needs to always run for the web server to work. SSL certificates need to be in the same folder as this Python script, named cert.pem and cert.key.

Remote commands in your visual novel

Commands starting with <remote_> in LVNAuth are used for sending or requesting remote data.

Here is an example: <remote_call: get sunny day scene script>

What you pass into the <remote_call> command are your own values. How you deal with the values on the server is to have code to deal with all the different requests you will send to the server from your visual novel.

In the remote_call line above, the action parameter I'm sending to the server is get sunny day scene script. This is a parameter that only my visual novel uses and doesn't mean anything for other visual novels.

So what will that line do? The server will see that the visual novel requested get sunny day scene script and on my LVNAuthWeb server, I'm specifically dealing with that request in Python.

Custom Python code

In LVNAuthWeb's folder, there is a Python script file named custom_vn.py.template. Use that file as a template for running custom Python code for your visual novel.

Rename custom_vn.py.template to custom_vn.py.

Inside that file, there is a function named run_custom_script. That's the Python script where you can deal with requests from your visual novel.

In the run_custom_script function, I have this:

if command_name == "get sunny day scene script":
    lvnauth_script = """<load_background: sunny day>
<load_object: beach ball>
""" 

The variable, lvnauth_script, will return an LVNAuth script back to the visual novel and will get read by my visual novel.

Passing argument values to LVNAuthWeb

There may be times when you want to pass values to LVNAuthWeb server.

Here is an example:

<remote_call: get the current weather, name=Theo, units=celcius>

The example line above is meant to be put in a visual novel (chapter script or scene script or reusable script).

The action is: get the current weather

The name of my character that should speak is Theo (name=Theo).

Then we specify whether the temperature should be shown in celcius or fahrenheit. In this case, I want to show the temperature in celcius (units=celcius).

This is what it would look like on the server (LVNAuthWeb) in the Python script file: vn_run.py, in the function: run_vn_script():

def run_custom_script(vn_name: str,
                      license_key: str,
                      episode: str,
                      command_name: str,
                      web_script: str,
                      vn_data: Dict) -> str | None:
    """
    Arguments:

    - vn_name: the name of the visual novel name that the license key is for.

    - license_key: the unique license key for the visual novel name (per user).

    - episode: the episode name or episode number.

    - command_name: the action purpose (command) of the request.
      This is specific to the visual novel. Example 'get score' or 
      'save answer'
      Those are just examples; a command can be any action specified by the
      visual novel author.

    - web_script: if an LVNAuth was generated in plpgsql (PostgreSQL), then 
      this variable will contain a value (most likely an LVNAuth script).
      Otherwise, it will be None.

    - vn_data: specific data that was passed in to the <remote> command in LVNAuth.
      Example:
      <remote: get score, team name=blue team>
      In the line above, 'get score' is the command name.
      The specific data is {'Team Name':'blue team'} (as a Dict).

    return: an LVNAuth script (str) or None
    """

    lvnauth_script = None

    if command_name == "get sunny day scene script":
        lvnauth_script = """<load_background: sunny day>
<load_object: beach ball>
"""
    elif command_name == "get the current weather":

        character_name = vn_data.get("name")

        temp_units = vn_data.get("units")
        if temp_units == "celcius":
            temperature = 23
        else:
            temperature = 73.4

            lvnauth_script = f"""<character_show: {character_name}_normal>
The current weather is {temperature} degrees {temp_units}"""

    except Exception:
        return "error-script"

    else:
        # No exception has occurred.
        return lvnauth_script

In my script, I hard-set the temperature values (because it's only an example). If this were to be used in a real visual novel, you could get the real temperature from an API somewhere using plain Python code.

The value in the variable, lvnauth_script, would get inserted into the current chapter or scene script.

Even though <remote_> commands can be used in a reusable script, it gets inserted into the active chapter script or scene script; whichever is active at the time. The reason is: remote web scripts like this can include character dialogue text and reusable scripts can't show character dialogue text.

Generating LVNAuth scripts with PL/pgSQL

You could use PostgreSQL to generate LVNAuth scripts. If you are more comfortable using SQL queries than writing Python code, this will allow you to create dynamic content for your visual novels in PostgreSQL.

The schema name is: custom_script and the name of the function is remote_request. Inside that function, the expected return value is your LVNAuth script as text.

So you could run your logic in the remote_request function and return an LVNAuth script.

An advantage to generating LVNAuth scripts inside the database rather than in Python is that you have the entire database to work with. However, Python provides other advantages, such as being able to connect to various different APIs. You can use Python to connect to the database too, but if your script is written inside the database, data retrieval should be quicker.

You can utilize both PL/pgSQL and Python in your projects or just one of them; or none at all if you're creating an offline visual novel.

Here are the primary functions you can use in your PL/pgSQL scripts.

Schema function Purpose
custom_script.remote_request Used for creating a custom LVNAuth script. The script gets returned to the playing visual novel.
save_data.save_vn_data Save text (such as the viewer's decisions in the story) to the database for a particular license key.
save_data.get_vn_data Get saved text (such as the viewer's saved decisions in the story) from the database for a particular license key.

Save a value

To save a value to the database, use the <remote_save> command in LVNAuth.

<remote_save: favcolor=Blue>

If an existing value already exists, it will be overwritten with the new value.

Multiple values can be saved with one command.

<remote_save: favcolor=Blue, favpet=Cat, favseason=Spring>

Get a value

To get a previously saved value and insert it into your story, use the <remote_get> command in LVNAuth.

<remote_get: favcolor>

The result will get inserted into the currently active chapter or scene script that's being played in your visual novel. If you run the above command in a reusable script, the result will be inserted into the active chapter or scene script (whichever is currently playing) - not the reusable script. This way you can get character dialogue text from the server and have it shown in the active chapter or scene. Reusable scripts can't display dialogue text, it will just be ignored.

Get a value into a variable

We can get the result from the database and put the value into a variable.

Like this: <remote_get: favcolor, my_variable>

The variable doesn't have to exist or be declared prior to using <remote_get>. If the variable doesn't exist, it will be created automatically.

Only one result can be retrieved at a time. So if you want to get two results, two commands will need to be used, like this:

<remote_get: favcolor, my_variable>
<remote_get: favseason, other_variable>

'remote_get' with a variable VS without a variable

When you want to check a saved result, use <remote_get> with a variable. If you want to insert a web generated LVNAuth script (such as dialogue text or generated LVNAuth scripts from the server), it might make more sense to insert the results directly to your actively playing chapter or scene script.

So to recap:

<remote_get: favcolor, my_variable>

The above line gets the already-saved value for favcolor and puts that value into a variable named my_variable.

<remote_get: favcolor>

The above line does almost the same thing except it doesn't put the value into a variable; it just adds the value to your active chapter or scene. The value would be any value that you saved before using <remote_save>.

Example of <remote_get> with a variable:

<remote_save: favcolor=purple, favpet=cat>

<remote_get: favcolor, color_choice>
<remote_get: favpet, pet_choice>

Is your favourite color ($color_choice)?
And your favourite pet is a ($pet_choice)?
<halt>

<case: color_choice, is not, orange>
I thought you would have
liked orange instead of ($color_choice).
<case_end>

<halt>

<case: pet_choice, is not, dog>
And I thought you would have
liked dogs instead of ($pet_choice)s.
<case_end>

<halt>

Although the example above would work, you wouldn't typically save a value and then get the value of the variable immediately after. Saving and getting data is useful when there is some time apart. For example, if your visual novel saves a value, you could retrieve that value again the next time the visual novel starts up. You could also retrieve a value during a different scene to branch out your story depending on the choices that your viewer made in earlier chapters and scenes.

Example of <remote_get> without a variable:

Not using a variable can be useful when you want to directly insert an LVNAuth script from a remote script to the active chapter or scene without putting the value into a variable.

Example:

<remote_save: favpet=cat>
<remote_get: favpet>

Which would just show 'cat' as dialogue text. The first line saves the value 'cat' into a save slot named 'favpet'.

The second line gets the value from the save slot, 'favpet' and puts it in your actively playing script.

The second line is the equivalent of this:

<remote_save: favpet=cat>
cat

So in the example above, it will show print out 'cat' as dialogue text, because that's what the save slot 'favpet' has, as a value in our example.

Where the saved data is stored

The data is stored in a PostgreSQL database. Each license key for a visual novel has its own save slot to save and retrieve saved information.

Shared license key VS Private license key

A shared license key is shared amongst all the viewers of the visual novel. It's integrated within the visual novel, so it doesn't need to be entered manually when the visual novel starts up (unlike a private license). Saving and retrieving data is possible, but the saved data for all the viewers of the visual novel will be the same. This type of license is useful for non-specific data-saving and when storing unique decisions is not required or not being used. For example, a visual novel that retrieves the latest news from your own website or gets information about one particular thing, regardless of who is viewing the visual novel.

A private license key is unique for each viewer of a visual novel. Each private license key has its own dedicated save slot. This type of license is not integrated within the visual novel - the user has to enter in a private license key when the visual novel first starts up (at least the first time the visual novel starts). Private licenses are useful for storing viewer decisions that will impact the direction of the story. Additionally, a private license key can also act as a DRM system. For example: to allow only paid viewers to be able to view a visual novel.

Last updated: Tue 12 August 2025