Tired of taking long scrolling screenshots? Want to upload all those awesome logs you have onto Neocities? You're in the right place!


First, create a new HTML file in Neocities and name it 'chats.html'. Here's the basic HTML structure for the chat room:

<!DOCTYPE html>
    <nav class="navbar">
    <div class="container" id="content"></div>
    <script src="generateChat.js"></script>

This is the base template I use. The '<nav>' section is just a placeholder to indicate your links to navigate around your webpage. The important bits are the bits below, the <div> and the <script>.

The <div> is the 'container' class with id 'content'. That is what will host your chatlog. The <script> is the part that links the Javascript file generateChat.js to your HTML page.

You will need BOTH for this to work!


Here is the CSS you can use to style your chat room. This is the CSS I have for Georgia and Anise's log, which you can view here. You can copy all this and save it to your Neocities. I like to have different styles per chat, but if you want to use the same styling everywhere, feel free to simply save this as 'chatstyle.css'. Keep in mind that you can feed this into GPT-4 and it can also come up with some neat CSS stylings for your webpage too!

/* Color of the base background and the font used on the page.*/
body {
  font-family: 'Georgia', serif;
  background-color: #f0f0f0;
  margin: 0;
  padding: 0;

/* Styling of the chatbox container.*/
.container {
  max-width: 800px;
  margin: auto;
  margin-left: 220px;
  padding: 20px;
  background-color: rgba(255, 255, 255, 0.95);
  border-radius: 10px;
  box-shadow: 5px 5px 15px rgba(0, 0, 0, 0.1);

/* Styling of each individual 'chat message'.*/
.box {
  background-color: rgba(232, 232, 232, 0.8);
  border-radius: 8px;
  margin-bottom: 20px;
  padding: 20px;
  display: flex;
  align-items: start;

/* Name of the bot speaking*/
.name {
  font-weight: bold;
  margin-bottom: 10px;
  color: #333;

/* Bot avatar styling */
.character-img {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  margin-right: 20px;

/* Text surrounded by quotes */
.quote {
  color: #333;

/* Text surrounded by asterisks */
.asterisk {
  font-style: italic;

/* Triple-backtick code blocks */
.preformatted {
  background-color: #2f4f4f;
  border: 1px solid #888;
  padding: 10px;
  color: #7fffd4;
  white-space: pre-wrap;
  white-space: -moz-pre-wrap;
  white-space: -pre-wrap;
  white-space: -o-pre-wrap;
  word-wrap: break-word;
  margin: 10px 0;
  font-family: 'Courier New', monospace;


Below is the full Javascript. Copy this entire thing and save it in the same folder as your HTML file as 'generateChat.js'. You'll need to make some changes for this to work, which I'll go over in more detail after this code block.

// The dictionary holding chat details
const chatDetails = {
  'drusilla': {
    'characters': ['Drusilla', 'Paul'],
    'stylesheet': 'log_styles/drusilla_style.css',
    'log': 'logs/drusilla.txt'
  'shoujorei': {
    'characters': ['ShoujoRei', 'Ana'],
    'stylesheet': 'log_styles/shoujorei_style.css',
    'log': 'logs/shoujorei.txt'

// Extract the 'name' parameter from the URL
let urlParams = new URLSearchParams(window.location.search);
let chatName = urlParams.get('name');

// If the chat name exists in the dictionary, generate the dialogue
if (chatName in chatDetails) {
  let details = chatDetails[chatName];
  generateDialogue(details.log, details.characters, details.stylesheet);
else {
  let content = document.getElementById('content');

  let errorMsg = document.createElement('p');
  errorMsg.innerText = 'Woah, what are you doing here? Looking for nonexistent logs? I feel ya. Nothing here, though!';
  let link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = 'log_styles/shoujorei_style.css'; //default to shoujorei because it's techy heh

function generateDialogue(textFile, characters, stylesheet) {
    .then(response => response.text())
    .then(data => {
      let lines = data.split('\n');

      let dialogue = "";
      let character = "";

      const characterPattern = new RegExp(`^(${characters.join('|')}):`);

      for (let i = 0; i < lines.length; i++) {
        if (characterPattern.test(lines[i])) {
          if (i !== 0) {
            addDialogue(character, dialogue);
          character = lines[i].split(':')[0];
          dialogue = lines[i].split(': ').slice(1).join(': ') + '
'; } else { dialogue += lines[i] + '
'; } } addDialogue(character, dialogue); }); function addDialogue(character, dialogue) { let div = document.createElement('div'); div.className = 'box'; let dialogueDiv = document.createElement('div'); let img = document.createElement('img'); img.className = 'character-img'; img.src = '../images/' + character + '.png'; div.appendChild(img); let name = document.createElement('p'); name.className = 'name'; name.innerText = character; dialogueDiv.appendChild(name); let para = document.createElement('p'); dialogue = dialogue.replace(/"(.*?)"/g, '"$1"'); // Quotes dialogue = dialogue.replace(/\*(.*?)\*/g, '$1'); // Asterisks dialogue = dialogue.replace(/```([^`]*?)```/gs, '
'); // Triple backticks para.innerHTML = dialogue; dialogueDiv.appendChild(para); div.appendChild(dialogueDiv); let content = document.getElementById('content'); content.appendChild(div); } // Update the stylesheet let link = document.createElement('link'); link.rel = 'stylesheet'; link.href = stylesheet; document.head.appendChild(link); }

Okay! Now let's get down to brass tacks.

Uploading the Files

First, let's take a gander at my neocities setup.

A screenshot of my Neocities setup
My Neocities setup

You can see that chats.html and generateChat.js are in the same directory. I also have three folders: images, log_styles, and logs. Let's take a look at each of them.

Contents of my images folder
images folder

My Images folder hosts the avatars of the characters.

Contents of my log_styles folder
log_styles folder

My log_styles folder contains the CSS files for each of my chats.

Contents of my logs folder
logs folder

My logs folder contains the .txt SillyTavern exports for the logs.

Let's say you want to upload your very first log. What you're going to do is:

1. Create the folders: NeoCities doesn't come with folders set up beforehand. Click on New Folder in the home directory, and create an images folder, a log_styles folder, and a logs folder.

2. Upload the images to the Images folder: Navigate to the Images folder and upload whatever images you want to use for the characters. They must be in PNG format, and they must match the names of the characters in the 'Characters' argument of the above dictionary. So for instance, if you have a chat between Esther and Anon, you must upload the images as 'Esther.png' and 'Anon.png' respectively.

3. Upload the log to the Logs folder: Navigate to the logs folder and upload the log txt file you've exported.

4. Upload the CSS to the logs_styles folder (Optional): If you're using a unique CSS theme for your new log, upload your unique CSS file to the logs_styles folder. You can skip this step if you're using the same CSS for everything.

Cool! Now you have everything you need. The next thing you're going to wanna do is go inside generateChat.js. In Neocities, hover and click on 'edit' to dive in. There is only one thing you'll need to change.

Updating The Chat Dictionary

This holds the chat data. As you can see, there are two entries here: Drusilla and ShoujoRei.

// The dictionary holding chat details
const chatDetails = {
  'drusilla': {
    'characters': ['Drusilla', 'Paul'],
    'stylesheet': 'log_styles/drusilla_style.css',
    'log': 'logs/drusilla.txt'
  'shoujorei': {
    'characters': ['ShoujoRei', 'Ana'],
    'stylesheet': 'log_styles/shoujorei_style.css',
    'log': 'logs/shoujorei.txt'

Let's break down the values.

1. Characters: This denotes the names of the characters involved in the chat. The names here MUST exactly match the names of the characters in the text file. So for instance, if you're playing around with Inquisitor Elyria, and you put in 'Elyria' as the character name in this array instead of 'Inquisitor Elyria', this will not work. You must put in 'Inquisitor Elyria'.

2. Stylesheet: This is the link to whatever CSS stylesheet you want to use for this chat. I like having different stylesheets for each chat, but if you're not into that, feel free to use the same stylesheet everywhere.

3. Log: This is the most important part: the link to the log file itself!

Now, if you happen to also have a Drusilla chat with characters named Drusilla and Paul, as well as a ShoujoRei chat with ShoujoRei and Ana, you'd be all set! But I suspect you may have different chats than me. So, you're going to have to update this dictionary to fit whatever your chats are. Remove Drusilla and ShoujoRei from your version of the file.

Now, remember all the files you uploaded before? Here's where they come into play. Let's continue using the Esther and Anon example before, and let's say you have your Esther log saved as 'vampire_loving.txt', and that you're using the same stylesheet for everything, 'chatstyle.css', which you've saved inside your log_styles folder.

Your new chatDetails dictionary will look like:

// The dictionary holding chat details
const chatDetails = {
  'esther': {
    'characters': ['Esther', 'Anon'],
    'stylesheet': 'log_styles/chatstyle.css',
    'log': 'logs/vampire_loving.txt'

Awesome! That's all you need to do. Now hit save, and let's move on to the next part...actually getting to your fancy new log page!

Navigating to the Log Page

Remember the first part of this tutorial, with the HTML sample and how we named it chats.html? You may be wondering how you can actually view your log, because all you see when you go to chats.html is an error message saying it can't find any logs. That's because chats.html needs to take in an argument in order to load the log.

Let's take a look at a snippet of HTML I have on my Logs page.

<h3><a href="/chats.html?name=drusilla">Log with Drusilla (heyshitkan)</a></h3>
<div class="desc">
  Paul's the bottom of the vampiric foodchain as a good-for-nothing Caitiff, with a ghoul who's dangerously unstable and addicted to his blood. Not that his sob story's gonna move the rent collectors. With him on the outs with the local Baroness, Paul and Drusilla turn to the next best option: petty cons.
  This is the bot that I developed the character of <a href="bots.html#paul">Paul</a> with. Check out the rest of heyshitkan's bots <a href="https://www.chub.ai/users/heyshitkan">here!</a>

In particular, note where the href in line 1 is pointing to. It's calling '/chats.html?name=drusilla'.

What this means is that it's passing in an argument 'name' with value 'drusilla' to the chats.html page. So how do you get to your new Esther log? Well, long story short, it corresponds to whatever is the key value you have in your character dictionary. So if the key value is 'esther', then the argument you pass in will be 'esther'. So, you'll have to call '/chats.html?name=esther'.

Voila! That should work!


It doesn't work! I see the same 'This log does not exist!' error!

Okay, let's go back to generateChats.js, and let's take a look at the chatDetails dictionary.

const chatDetails = {
  'esther': {
    'characters': ['Esther', 'Anon'],
    'stylesheet': 'log_styles/chatstyle.css',
    'log': 'logs/vampire_loving.txt'

The argument that you pass in as the name should EXACTLY match the key value of the log you want to access. What is the key value for 'esther'? In this case, the key value is Line 2 of the code block, 'esther'. Still unclear? Let's take a look at the ShoujoRei and Drusilla dictionary.

const chatDetails = {
  'drusilla': {
    'characters': ['Drusilla', 'Paul'],
    'stylesheet': 'log_styles/drusilla_style.css',
    'log': 'logs/drusilla.txt'
  'shoujorei': {
    'characters': ['ShoujoRei', 'Ana'],
    'stylesheet': 'log_styles/shoujorei_style.css',
    'log': 'logs/shoujorei.txt'

Okay. In this case, the key value of the Drusilla log is on line 2, 'drusilla'. And the key value of the ShoujoRei log is on line 7, 'shoujorei'. Thus, the argument you want to pass in to access the Drusilla log would be '/chats.html?name=drusilla', and the argument for the ShoujoRei log would be ''/chats.html?name=shoujorei'.

If you are still experiencing this issue, please reach out to me at oeufvivant@protonmail.com!

My chat images are broken. Why?

There are a couple of possibilities for this.

Firstly, the Javascript code is hardcoded to assume that your chat images are PNG files. If they're JPGs, then this won't work (however, you can always update the Javascript code to assume JPG instead, or what have you!).

Secondly, the filenames of the image files MUST exactly match the names of the characters in the character array of the dictionary. And yes, it is case-sensitive!

If neither of these seem to be the issue, shoot me an email and I'll try to help out.

Wait, images are tied to character names? That sucks! What if I want to use different avatars for the same character across logs?

Yeah, I know. When making this setup, I wanted to make it as easy and as quick to start up as possible. Later, I may create a more advanced version that takes into account different avatars across logs for the same character.

I'm getting weird errors. Nothing works. My Neocities is blowing up. What's going on?

The changes here should not affect any of your other Neocities pages, and should be contained only to when you access 'chats.html', unless you did something strange. If you think it's something from this tutorial, shoot me an email at oeufvivant@protonmail.com with an attached screenshot and output from Developer Tools (F12 -> Console).