Home / Blog

Create an Animated Walking Character in Articulate Storyline And use JavaScript to make it move - Part 1

By Nathan Stoker · June 5, 2025 · 12 min read

Create an Animated Walking Character in Articulate Storyline And use JavaScript to make it move - Part 1

How to Create an Animated Walking Character in Articulate Storyline

Complete Step-by-Step Tutorial

Learn how to build a smooth, keyboard-controlled animated character using PNG frames, JavaScript, and Storyline's built-in features. This comprehensive guide covers everything from preparing sprite frames to implementing movement logic.

Once you have done this tutorial, head over to Part 2.

What You'll Build

By the end of this tutorial, you'll have:

•        A character that walks smoothly when you press the arrow keys

•        Proper animations that don't jitter or jump around

•        Speed that gradually increases the longer you hold a key

•        Different character states for walking left, walking right, and standing still

•        Professional-looking animations suitable for e-learning courses

Why This Animation Approach Works

•        No jittery movement – We align every frame perfectly to prevent sliding

•        Smooth animations – APNG format keeps transparency and timing intact

•        Easy to manage – Storyline handles visual states while JavaScript handles movement

•        Professional feel – GSAP library makes everything buttery smooth

•        Reusable – Once set up, you can easily swap in different character sprites

What You Need Before Starting

•        Basic knowledge of Articulate Storyline (creating states, adding images, setting up triggers)

•        Character sprite frames (we'll show you where to get free ones)

•        A tool to create animated PNGs (free options available)

•        The GSAP animation library (already included in modern Storyline versions)

Free Character Sprites

For this tutorial, we'll use a free cat character sprite. You can download it from:

Hand-drawn Animation Frames Collection on Freepik

This collection includes six walking frames that we'll use to create our animated character.

Step 1: Prepare Your Character Sprite Frames

Download and Organize Your Files

1.     Download the character sprite collection from the Freepik link or use your own six PNG files showing different walking poses

2.     Create a dedicated folder called "block-cycle" and extract all six images inside

3.     Rename the files consistently – Use names like walk-right-01.png through walk-right-06.png

4.     Identify the longest frame – Look through all six frames and find the one that extends furthest (usually frame 6)

Align Every Frame Perfectly (Critical Step)

This is the most important step for smooth animation. If your frames aren't perfectly aligned, your character will appear to slide or jitter during movement.

1.     Open your graphics editor (Photoshop, Illustrator, GIMP, etc.) and load all six character frames

2.     Start with the longest frame – This is usually frame 6, which often has the most extended leg pose

3.     Center this frame in your workspace as your reference point

4.     Enable the shadow layer – Most sprite collections include a shadow beneath the character. Use this as your alignment guide

5.     Zoom in closely (Ctrl+Y in most editors) to see individual pixels clearly

6.     Identify your reference point – Look for a consistent spot like the shadow, foot position, or a marking on the character

7.     Align each remaining frame – Open frames 1-5 and drag each so the reference point snaps to exactly the same position

8.     Remove the shadow layer once all frames are aligned

9.     Verify consistent dimensions – All frames must be exactly the same width (e.g., 240px)

Pro Tip: If one frame exports at 239px while others are 240px, your entire animation will jitter. Always double-check dimensions before proceeding.

Step 2: Create Your APNG Animation

Export Individual PNG Frames

1.     Use your editor's batch export feature (File → Export → Export for Screens in Adobe products)

2.     Select all six aligned frames

3.     Set the width to exactly 240px (or whatever consistent size you chose)

4.     Export to your "block-cycle" folder

5.     Name them sequentially: walk-right-01.png through walk-right-06.png

6.     Verify dimensions – Right-click each PNG and confirm they're all exactly 240px wide

Download and Use APNG Assembler

For creating our animated PNG, we'll use a free, reliable tool:

Download APNG Assembler from SourceForge

This tool converts your individual PNG frames into a single animated PNG file that maintains transparency and smooth playback.

Assemble Your Walking Animation

1.     Launch APNG Assembler after installation

2.     Add your six PNGs in order (01 through 06) – the sequence is crucial for proper animation flow

3.     Set frame timing: Select "Delay all frames" and set to 2/24 seconds per frame (or 1/12) for smooth, natural walking speed

4.     Export your APNG: Choose your "block-cycle" folder and name it something like "walk-right-block.png"

5.     Test the animation – Drag the APNG into a browser tab to verify it loops smoothly without jumping

Step 3: Set Up Storyline Character States

Create the Character Container Object

1.     Open your Storyline project and navigate to your target slide

2.     Insert a shape (Insert → Shapes → Rectangle) – this will be your character container

3.     Position the rectangle where you want your character to appear on screen

4.     Rename the object in the Selection Pane to "Character" (we'll need this name later)

5.     Add a temporary text box inside the rectangle that says "Character State" so you can see its position

6.     Delete the text box once you've positioned the container correctly

Positioning Tip: Don't worry about perfect horizontal centering – the JavaScript will handle final positioning. Focus on getting the vertical position right.

Set Up the Character State Variable

Before adding character images, create the variable that will control state changes:

1.     Open the Variables panel (usually in the bottom-right)

2.     Create a new variable:

Name: characterState (exactly as shown – case sensitive)

Type: Text

Default value: Leave blank

3.     Click OK to save the variable

Import Character Images as States

1.     Select your "Character" rectangle

2.     Go to Format → States → Edit States in the ribbon

3.     Click "Insert Pictures as States" (this imports multiple images as different states)

4.     Select your character images: idle-right, idle-left, walk-right, walk-left

5.     Click "Done" to finish creating states

Step 4: Configure State-Change Triggers

These triggers are what make your character animation work. They listen for changes to your characterState variable and automatically switch the visual appearance.

Create the Four State-Change Triggers

Set up four individual triggers—one per character state. Each trigger listens for a variable change and updates the character’s visual state accordingly.

Trigger

Action

When

Condition

Walking Right

Change state to WalkRight

Variable changes

characterState = "WalkRight"

Walking Left

Change state to WalkLeft

Variable changes

characterState = "WalkLeft"

Idle Right

Change state to IdleRight

Variable changes

characterState = "IdleRight"

Idle Left

Change state to IdleLeft

Variable changes

characterState = "IdleLeft"

Each trigger ensures the character immediately reflects the current value of the characterState variable.

Important Trigger Setup Notes

•        Trigger Location: Make sure these triggers are at the slide level, not attached to specific objects

•        Exact Naming: The condition values ("WalkRight", "IdleRight", etc.) must match exactly what the JavaScript will send

•        Variable Name: Ensure all triggers reference the same variable: "characterState"

Testing Tip: After setting up triggers, you can test them by manually changing the characterState variable value in Storyline's preview mode to see if states switch correctly.

Step 5: Add the JavaScript for Character Movement

Get Your Character's Data-Model-ID

Storyline assigns a unique identifier to each object. To make our JavaScript work, we need to find your character's ID:

1.     Right-click your "Character" object in Storyline

2.     Look at the top of the context menu – you'll see the data-model-id displayed

3.     Click the copy option next to the ID to copy it to your clipboard

4.     Keep this ID handy – you'll paste it into the JavaScript code

How to Add the JavaScript

•        Go to the Triggers panel in Storyline

•        Create a new trigger:

Action: Execute JavaScript

When: Timeline starts on this slide

Script: Paste the complete code below

•        Save and publish your project

The Complete JavaScript Code

Copy and paste this entire code block into the Execute JavaScript trigger:

 (function () {
  try {
    // ─── Constants for movement ───────────────────────────────────────
    const moveSpeedBase = 2;       // Starting pixels/frame
    const accelerationStep = 0.5;   // Increase in speed each step
    const maxSpeed = 15;           // Cap on pixels/frame
    // ─── State variables ────────────────────────────────────────────
    let currentSpeed = moveSpeedBase;
    let moveInterval = null;
    let lastDirection = 'right';
    let isMoving = false;
    // ─── Storyline Variable Name ────────────────────────────────────
    // (Must match exactly the Text variable you created in Storyline)
    const STATE_VAR_NAME = 'characterState';
    // ─── Data-model-id for the character sprite ─────────────────────
    // Replace '6TZmBakju4p' with your actual character object's data-model-id
    const MODEL_IDS = {
      character: '6TZmBakju4p'
    };
    // ─── Grab the character element from DOM ─────────────────────────
    const character = document.querySelector[data-model-id="${MODEL_IDS.character}"]);
    // ─── Dependency Checks ───────────────────────────────────────────
    if (!character) {
      console.error❌ Character element not found (data-model-id='${MODEL_IDS.character}').);
      return;
    }
    if (!window.gsap) {
      console.error("❌ GSAP library not found. Make sure GSAP is loaded before this script runs.");
      return;
    }
    // ─── Initial CSS Setup ───────────────────────────────────────────
    character.style.position = 'absolute';
    character.style.visibility = 'hidden';
    character.style.left = '50%'; // Temporary centering; final centering happens after layout stabilizes
    // ─── Find a valid "stage" container (ancestor with nonzero width) ──
    let container = character.parentElement;
    let stageContainer = null;
    let depth = 0;
    while (container && depth < 15) {
      if (container.offsetWidth > 0) {
        stageContainer = container;
        break;
      }
      container = container.parentElement;
      depth++;
    }
    if (!stageContainer) {
      console.error("❌ No valid stage container found.");
      return;
    }
    // ─── Function: Wait until Storyline's layout is ready ─────────────
    function waitForLayout(callback) {
      let tries = 0;
      function check() {
        if (character.offsetWidth > 0 && stageContainer.offsetWidth > 0) {
          // Optionally confirm that GetPlayer exists before invoking SetVar
          if (window.GetPlayer && typeof window.GetPlayer().SetVar === 'function') {
            callback();
          } else if (tries++ < 30) {
            setTimeout(check, 50);
          } else {
            console.error("❌ Storyline player never initialized. Proceeding anyway.");
            callback();
          }
        } else if (tries++ < 30) {
          setTimeout(check, 50);
        } else {
          console.error("❌ Layout never stabilized.");
        }
     }
    check();
    }
   // ─── After layout stabilizes, center character and initialize state ─
    waitForLayout(() => {
     const stageWidth = stageContainer.offsetWidth;
      const charWidth = character.offsetWidth;
      const centerX = stageWidth / 2 - charWidth / 2;
      // Center character horizontally and make it visible
      gsap.set(character, { left: centerX });
     character.style.visibility = 'visible';
     // Set initial Storyline variable: characterState = 'idleRight'
    if (window.GetPlayer && typeof window.GetPlayer().SetVar === 'function') {
        try {
         GetPlayer().SetVar(STATE_VAR_NAME, 'idleRight');
       } catch (err) {
          console.warn⚠️ Failed to set initial ${STATE_VAR_NAME}: ${err.message});
        }
    }
      // ─── Helper: get current left position of character (in px) ─────
      function getCharacterLeft() {
        return parseFloat(character.style.left || '0') || 0;
      }
      // ─── handleMove(direction): updates position & acceleration ─────
      function handleMove(direction) {
        const charLeft = getCharacterLeft();
        // If we just started moving or changed direction → update storyboard state
        if (!isMoving || direction !== lastDirection) {
          if (window.GetPlayer && typeof window.GetPlayer().SetVar === 'function') {
            try {
              const state = direction === 'left' ? 'walkLeft' : 'walkRight';
              GetPlayer().SetVar(STATE_VAR_NAME, state);
            } catch (err) {
              console.warn⚠️ Failed to set ${STATE_VAR_NAME} to ${direction}: ${err.message});
            }
          }
          lastDirection = direction;
          isMoving = true;
        }
        // Compute new left value
        const newCharLeft =
          direction === 'left'
            ? charLeft - currentSpeed
            : charLeft + currentSpeed;
        // Animate the character's left property over 0.05s (for smoothing)
        gsap.to(character, { duration: 0.05, left: newCharLeft, overwrite: true });
        // Increase speed (up to maxSpeed)
        currentSpeed = Math.min(maxSpeed, currentSpeed + accelerationStep);
      }
      // ─── stopMove(): clear interval, reset speed, switch to idle pose ──
      function stopMove() {
        if (moveInterval) {
          clearInterval(moveInterval);
          moveInterval = null;
          currentSpeed = moveSpeedBase;
          if (isMoving) {
            if (window.GetPlayer && typeof window.GetPlayer().SetVar === 'function') {
              try {
                const idleState = lastDirection === 'left' ? 'idleLeft' : 'idleRight';
                GetPlayer().SetVar(STATE_VAR_NAME, idleState);
              } catch (err) {
                console.warn⚠️ Failed to set idle ${STATE_VAR_NAME}: ${err.message});
              }
            }
            isMoving = false;
            lastDirection = lastDirection || 'right';
          }
        }
      }
      // ─── Keydown listener: start moveInterval on ← or → ───────────────
      document.addEventListener('keydown', function (e) {
        const key = e.key.toLowerCase();
        let direction = null;
        if (key === 'arrowleft') direction = 'left';
        if (key === 'arrowright') direction = 'right';
        if (direction && !moveInterval) {
          // Run handleMove(direction) every 20ms → smooth continuous movement
          moveInterval = setInterval(() => handleMove(direction), 20);
          e.preventDefault();
        }
      });
      // ─── Keyup listener: stop movement on key release ────────────────
      document.addEventListener('keyup', function (e) {
        const key = e.key.toLowerCase();
        if (key === 'arrowleft' || key === 'arrowright') {
          stopMove();
          e.preventDefault();
        }
      });
    });
  } catch (err) {
    console.error❌ Script failed during initialization: ${err.message});
    console.error(err.stack);
  }
})();

Key Customization Points

The JavaScript includes several settings you can easily adjust:

•        moveSpeedBase = 2 – Starting movement speed (lower = slower start)

•        accelerationStep = 0.5 – How quickly speed increases (lower = more gradual)

•        maxSpeed = 15 – Maximum movement speed (higher = faster top speed)

•        STATE_VAR_NAME – Must match your Storyline variable name exactly

•        MODEL_IDS.character – Replace '6TZmBakju4p' with your actual data-model-id!

Step 6: Test and Troubleshoot

Quick Test Checklist

•        Publish and open your slide in Chrome or Firefox

•        Check the console (F12) for any error messages

•        Verify the character appears centered on the slide in idle pose

•        Press the right arrow – character should start walking right and speed up

•        Release the key – character should stop and return to idle

•        Try the left arrow – same behavior but facing left

Common Issues and Fixes

Character not appearing:

•        Check that your data-model-id matches exactly

•        Make sure GSAP is loaded before the script runs

•        Verify your character container is visible on the slide

States not changing:

•        Double-check that your variable is named "characterState" exactly

•        Make sure all four state-change triggers are set up correctly

•        Confirm your states are named IdleRight, IdleLeft, WalkRight, WalkLeft

Jerky animation:

•        Verify all PNG frames are exactly the same width

•        Check that your APNG loops smoothly when viewed alone

•        Make sure frame alignment was done correctly

Keys not working:

•        Ensure the slide has focus (click on it first)

•        Check browser console for JavaScript errors

•        Try refreshing the page 

Final Checklist

Before showing your finished character to others, verify:

☐     All PNG frames are exactly the same width

☐     APNG loops smoothly when viewed alone

☐     Storyline variable "characterState" exists

☐     Four state-change triggers are set up correctly

☐     Data-model-id in JavaScript matches your character

☐     GSAP library is loaded before your script

☐     Character appears centered and responds to arrow keys

☐     States change properly (walking when moving, idle when stopped)

☐     No console errors appear in browser developer tools

What's Next?

Once you have this basic setup working, you can expand it with:

•        Background scrolling that moves opposite to the character

•        Jump and attack animations triggered by other keys

•        Touch controls for mobile devices

•        Multiple characters or interactive objects

•        Collision detection with boundaries or obstacles

The foundation you've built here – combining Storyline's state management with JavaScript's movement logic – can support much more complex interactive experiences. Happy animating!