StoryScript Blog

This blog will keep you updated on new StoryScript developments and share tips & tricks on how to make things work with the tool.

  • StoryScript 1.1.0 - Introducing media effects

    Sunday, November 1, 2026

    StoryScript 1.1.0 was just released! Building on experiments I did for a proof-of-concept, it is now possible to play sounds and animations during combat! Moving forward, I want to make this functionality available more broadly in StoryScript. Hence I set it up to be prepared to do so.

    Characters, Enemies, Persons and Items now have a new property, mediaEvents. It's a TypeScript record which you can populate with named events that you can then trigger from the game object via the game.commands.playMediaEffect function. A mediaEvent has four properties at the moment: delay, image, sound and animation. You specify one or more of these, and they will be executed in parallel. So I can now play a sound and show an animation on top of the portrait of an enemy when I attack him. Combat will proceed after the sound and animation are completed, whichever takes longest determines the delay.

    In code, you add the functionality to your combat system like this. The combat code shown is from the MyRolePlayingGame developed in the tutorials:

    const character = combatRound.characters[0] as Character;
    const enemy = combatRound.enemies[0];
    const damage = game.helpers.rollDice(combatRound[0].item?.attack ?? 0) + character.strength + game.helpers.calculateBonus(character, 'damage');
    
    // This is the new line of code that will play the 'attack' media event, using either the attack event from the item used by the character or the 
    // character in case no item is used, targeting the enemy.
    await game.commands.playMediaEffect('attack', combatRound[0].item ?? character, enemy);
    
    game.logToCombatLog('You do ' + damage + ' damage to the ' + enemy.name + '!');
    enemy.currentHitpoints -= damage;

    On the Sword item of the MyRolePlayingGame, I added an 'attack' mediaEvent like this, which will play the swing sound while at the same time showing the sword animation on top of the bandit picture:

    export function Sword() {
        return Item({
            name: 'Sword',
            description: description,
            attack: '1d6',
            equipmentType: EquipmentType.RightHand,
            value: 5,
            targetType: TargetType.Enemy,
            mediaEvents: {
                attack: {
                    sound: 'swing.mp3',
                    animation: {
                        sheet: 'sword_anim.png',
                        height: 48,
                        width: 48,
                        steps: 6,
                        speed: 1,
                        iterations: 1
                    }
                }
            }
        });
    }

    This will make your combat much more exiting! You can also add a media event for when you defeat the bandit, adding even more flavour. I hope you'll find it useful and can put the new feature to good use.

  • StoryScript as an NPM Package

    Friday, October 30, 2026

    Finally, the long awaited milestone of publishing StoryScript as a NPM package has been reached! You can now install StoryScript from NPM instead of having to fork the repository. No more git trouble keeping your fork updated, and much less files in your project that you don't need or don't want to change for your own work. It was quite a bit of work to create a good setup experience, but I'm very happy with the results.

    Check out the quickstart to see how you can setup a new StoryScript project using the package. If you have a StoryScript project already, you can switch with these steps:

    1. Setup a new project and copy your game's folder to the new project.
    2. Delete the run.ts file from your game's folder.
    3. Replace all occurrences of 'storyScript/Interfaces/storyScript' in your game's folder with 'storyScript'.
    4. Replace all other nested references to 'storyScript' with just 'storyScript'.

    That should do it!

    With the new package comes a new StoryScript CLI (Command Line Interface). I have updated this website to use the new commands. Instead of starting commands in your terminal with 'npm run', you now use 'npx storyscript'. Run 'npx storyscript' without any other arguments will show you an overview of all the commands available. It also reminds you of the code snippets available, with a link to the reference page on which these are listed.

    While I was at it, I made some pieces of StoryScript opt-in instead of available right away. When you don't need unit tests, no Vitest testing code will be present. And without a need to publish executables, electron won't be installed. But they are added for you as soon as you execute a command that requires them.

    I am hopeful the new package will make yyour StoryScript experience much more enjoyable!

  • Animations in StoryScript

    Sunday, October 11, 2026

    Although StoryScript has never focused on the visuals of computer games, the capabilities it has for working with images been increasing. When StoryScript started over 10 years ago, you could work with images by just including them in your HTML files. Later, image maps were added for creating visual adventure games, and after that the option to display images for features on top of image maps. Until now, these images have been static (apart from using .gif images, of course). But now, you also have the option to add simple animations using just a sprite sheet.

    To illustrate the concept, I've enriched the Visual Adventure Game demo with a pulsating light shown just above the fountain. This healing light is an item used as a feature. Check out the tutorial on visual adventure games to learn more about using features in a visual way. The difference between it and the herbs item found in the second location is that the healing light item has an animation instead of a picture:

    export function HealingLight() {
    	return Item({
    		name: 'Healing light',
    		animation: {
    			sheet: 'candlelight_anim.png',
    			height: 48,
    			width: 48,
    			steps: 6,
    			speed: 1,
    		},
    		equipmentType: EquipmentType.Miscellaneous,
    		interactive: false
    	});
    }

    This is the sprite sheet used:

    As you see, apart from the sprite sheet you need to specify the dimensions of a single sprite, the total sprites present and the animation speed. The engine uses this to display the sprites one after another, creating a moving image. I plan to add more options to this feature so you can also trigger the animation yourself. With that, you can randomize when the animation plays to for example have leaves moving in the wind. Simple additions, but they do a lot to make your world seem more alive.

    Finally, note that there is a new flag available for features, interactive. I added it to be able to add animated decorations as features but without being able to interact with them. Thus, unlike the other features in the demo, nothing happens when you hover over or click on the healing light. Of course, you can also use this flag to change the interactivity of a feature in your world if you come up with a use case for that.

     

  • StoryScript games as Progressive Web Apps

    Sunday, October 11, 2026

    Progressive Web Apps (PWAs) have been around for some time now. This technology allows a user to install a web app on a device and amongst other things use the app when not connected to the Internet when it has been made with offline support. PWAs are fully supported by all Chromium-based browsers (such as Chrome and Edge), other browsers offer more limited support. You can find an overview of the level of PWA support by browsers here.

    For StoryScript a PWA offers two main benefits:

    1. Show the game full screen without a browser UI, making the game feel more native to the device.
    2. Allow playing the game when offline or when the website offering the game is unreachable.

    Today, I added PWA support for StoryScript. It will be available automatically when you create a new game using the create game command:

    npx storyscript game YourGameName

    You'll find a new folder in your game's directory, called webroot. This folder contains four files, the gameinfo.json that used to be in the game's root directory and three files to display the logo for your game when installed on a device and in the PWA top bar: logo512.png, logo192.png and favicon.ico. The latter file is also shown in the browser's address bar.

    When you already have a game, you can update it in two steps:

    1. Copy over the webroot folder from the _GameTemplate folder to your own game's folder.
    2. Move the gameinfo.json file from your game's folder to the new webroot subfolder, replacing the one in there.

    You'll want to replace the logo files to users see your game's own logo and not the default StoryScript one.

    Now that your game is ready to be installed as a PWA, here's how you do it in Chrome. First find the Install page as app... menu option:


     

    Second, choose a name for the app:


    Press next and complete any additional options you might be presented with.

    When the app is installed, you have the option to open it in a tab. Do so. You can see that your game's interface is much cleaner now:

    Before   After
     

     

    Also, when you now turn off your internet connection and reload your game (you can do that by clicking on the icon at the top right and choosing reload page), it should work as usual!

  • Chromium-bug crashes Chromium browsers on Visual Adventure Game

    Friday, October 9, 2026

    I found and reported that the latest versions of Chromium-based browsers crash when hovering over a feature in StoryScript's Visual Adventure Game! This is a Chromium regression bug, not a bug in StoryScript. It was assigned the highest priority, 0, for fixing. Which means it should be fixed in the next version of Chromium and released soon.

    Not a good thing to find that your application crashes with nothing you can do about it, but happy that StoryScript is considered important enough to be assigned a priority 0 😆!