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

How to Add the JavaScript to Your Storyline Project

  1. Go to the Triggers panel in Storyline
  2. Create a new trigger:
    • Action: Execute JavaScript -# 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 will teach you everything from preparing sprite frames to implementing movement logic.

What You’ll Build

By the end of this Articulate Storyline 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 the visual states while JavaScript handles the 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 Your Animation

For this tutorial, we’ll use a free cat character sprite that works perfectly for learning. 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. Once you’re comfortable with the process, you can apply the same techniques to any character sprite.


Step 1: Prepare Your Character Sprite Frames

Download and Organize Your Character Files

  1. Download the character sprite collection from the Freepik link above 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 has the most extended pose)

Align Every Frame Perfectly (Critical Step)

This is the most important step for smooth Storyline character 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. We’ll use this as our 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:
    • A specific point on the shadow
    • The character’s foot position
    • A dot or marking on the character
  7. Align each remaining frame:
    • Open frames 1-5 one by one
    • Drag each frame so the reference point snaps to exactly the same position
    • Use your editor’s rulers or grid to ensure pixel-perfect alignment
  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

Important: Sometimes editors will export at 239px due to rounding. If this happens, manually set the width to 240px to prevent animation issues.

Download and Use APNG Assembler

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

Download APNG Assembler from: https://sourceforge.net/projects/apngasm/

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”
    • Set to 2/24 seconds per frame (or 1/12) for smooth, natural walking speed
    • This timing works well for most character animations
  4. Export your APNG:
    • Choose your “block-cycle” folder as the destination
    • Name it something clear like “walk-right-block.png”
  5. Test the animation – Drag the APNG into a browser tab to verify it loops smoothly without jumping

Quality Check: Your character should walk in place smoothly with no visible jumping or sliding between frames.


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 Storyline Character State Variable

Before adding our character images, we need to 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 Multiple States

Now we’ll add our character images directly as states of our container object:

  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:
    • Choose your individual PNG frames (not the APNG)
    • You’ll want at least: idle-right, idle-left, walk-right, walk-left
  5. Click “Done” to finish creating states

Note: The state names must match exactly what we’ll use in the JavaScript code later.


Step 4: Configure Storyline 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 of your character.

Create the State-Change Triggers

You need to create four separate triggers – one for each character state. Here’s how to set up each one:

Trigger 1 – Walking Right:

  • Action: Change state of Character to WalkRight
  • When: Variable changes → characterState
  • Condition: When characterState equals “WalkRight”

Trigger 2 – Walking Left:

  • Action: Change state of Character to WalkLeft
  • When: Variable changes → characterState
  • Condition: When characterState equals “WalkLeft”

Trigger 3 – Idle Right:

  • Action: Change state of Character to IdleRight
  • When: Variable changes → characterState
  • Condition: When characterState equals “IdleRight”

Trigger 4 – Idle Left:

  • Action: Change state of Character to IdleLeft
  • When: Variable changes → characterState
  • Condition: When characterState equals “IdleLeft”

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 name: “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

The Complete JavaScript Code for Character Animation

Create an “Execute JavaScript” trigger and paste this complete code:

 (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 '5mGJNQeqNPN' 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 in the Code

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 = ‘characterState’: Must match your Storyline variable name exactly
  • MODEL_IDS.character: Replace with your actual data-model-id

Important: Don’t forget to replace ‘PASTE_YOUR_ID_HERE’ with the actual data-model-id you copied from your character object!

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

This new Storyline feature makes it much easier than the old method of inspecting published HTML!

How to Add the JavaScript

  1. Go to the Triggers panel in Storyline
  2. Create a new trigger:
    • Action: Execute JavaScript
    • When: Timeline starts on this slide
    • Script: Paste the complete code above
  3. Save and publish your project

Step 6: Test and Troubleshoot

Quick Test Checklist

  1. Publish and open your slide in Chrome or Firefox
  2. Check the console (F12) for any error messages
  3. Verify the character appears centered on the slide in idle pose
  4. Press the right arrow – character should start walking right and speed up
  5. Release the key – character should stop and return to idle
  6. 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:

  • 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!