Forum Discussion
Controlling Illustrated Characters via JS?
I haven't had much professional call to use the illustrated characters much, but I do appreciate them, especially after learning about the expressions, poses, and perspectives that can all be LEGO'd together. A huge variety of characters and poses can be created, especially since "off-label" uses multiply their potential.
Being able to control all of those with JavaScript would enable us to develop better narrative training more efficiently. I've been stumbling around a bit with the obj.state assignment but neither 'happy' nor '_happy' work, so I'm probably grasping in the dark.
I do get that I can create states of an image and build the combinations of poses, expressions, and perspectives in there, but then they'll be cemented, won't they?
If anyone instead knows a way to dynamically change a character's expression, perspective, or pose with JS--or if this is on the way--I think we may have the beginnings of a visual novel engine... 🤔
You can use the object().state property to set slide objects to either the "Normal" state or a custom state. When you add characters to the slide, they adopt the Normal state and whatever pose/expression you selected is assigned to that state. Under the States tab, you can edit and add additional states for a slide object. You can select the character state you desire from the list of possible states, and add that to the state list. The names will match the list, so something like "Surprised", or "Disappointed", etc.
When adjusting states in JavaScript, use Normal to set the normal state, or use one of the custom state names you added (matching the case of the name). You have to explicitly add the state to the list for it to be available from JavaScript.
Note: When testing from the developer panel, if an invalid state was assigned to the object, for example object("####").state = "Default", then all later state assignments also failed, even if correct. I don't know if this issue persists within Storyline project triggers, but it may be a bug and should be kept in mind. If you accidentally assign an invalid state via JavaScript, then they may stop working altogether.
Also, you can assign a Disabled or _disabled state, but I'm not sure what that does since it does not actually become disabled. You can also assign a Hidden state, but if you do you can't unassign it via JavaScript. It just stays hidden.
5 Replies
- NedimCommunity Member
Hi Andrew,
Part 1All custom states should be written exactly as they appear in the Object States panel—no underscores are needed. For example, in your case, the custom state is "Happy".
Underscores are only required for default states such as Selected, Hover, Visited, and Down. These must be written with the "default" prefix, like so:
- _default_Selected
- _default_Hover
- _default_Visited
- _default_Down
This also applies when combining multiple default states during interactions, assuming all relevant states are activated in the Object States panel.
Note:
- The Normal state remains simply "Normal" (no prefix or underscores).
- The Disabled state is written as "Disabled".
I used a simple script executed when the timeline starts, as well as for enabling and disabling a button.
const button1 = object('5pyrnxdTyGJ'); setInterval(() => { setVar('currState', button1.state); }, 100);
I also used a separate trigger attached to another button to enable and disable the target button.
const button1 = object('5pyrnxdTyGJ'); button1.state = 'Disabled'; // when a disable/enable button is Selected // else const button1 = object('5pyrnxdTyGJ'); button1.state = 'Normal';
Part 2
I'm not entirely sure what you meant by dynamically changing expressions. The JavaScript API for Storyline only exposes the current state of an object, not the full list of available states.
To cycle through all states, you need to dig deeper into Storyline’s internal structure. I used a custom recursive function to locate the object by its ID and extract its states from nested properties. Then, I created a simplified version containing only the properties I needed, resulting in an array of all available states. This function is reusable—the only change you need to make is updating the object ID (e.g., '6fO7ipghgcH') to retrieve the available states for your next target object.
In the example below, the first button retrieves all available expression states for my character, and the second button cycles through them every second by setting object('id').state = stateName.
If you'd like to dig deeper, I can attach my Storyline file for you to review and test.
Part 3
To avoid writing complex code while achieving the same result, you might consider renaming your states using simple numbers—for example, from "1" to "8". Then, you can use a simple function to iterate through these states by setting the object's state to a number that increases at a regular interval (e.g., every second). The numeric value should match the name of each state, allowing the function to dynamically set the object’s state based on that number. You might even be able to squeeze in some form of GSAP or web animation between the intervals to create smoother transitions between states.const rectangle = object('6cn5CQM1j6n'); let num = 1; setInterval(() => { if (num <= 8) { rectangle.state = `${num}`; } else { rectangle.state = 'Normal'; num = 0; } num++; }, 1000);
- PhilMayorSuper Hero
Agree with Nathan on the states they only exist if the are created as states, it would cause a bit of bloat if they were all included and never used.
- Nathan_HilliardCommunity Member
You can use the object().state property to set slide objects to either the "Normal" state or a custom state. When you add characters to the slide, they adopt the Normal state and whatever pose/expression you selected is assigned to that state. Under the States tab, you can edit and add additional states for a slide object. You can select the character state you desire from the list of possible states, and add that to the state list. The names will match the list, so something like "Surprised", or "Disappointed", etc.
When adjusting states in JavaScript, use Normal to set the normal state, or use one of the custom state names you added (matching the case of the name). You have to explicitly add the state to the list for it to be available from JavaScript.
Note: When testing from the developer panel, if an invalid state was assigned to the object, for example object("####").state = "Default", then all later state assignments also failed, even if correct. I don't know if this issue persists within Storyline project triggers, but it may be a bug and should be kept in mind. If you accidentally assign an invalid state via JavaScript, then they may stop working altogether.
Also, you can assign a Disabled or _disabled state, but I'm not sure what that does since it does not actually become disabled. You can also assign a Hidden state, but if you do you can't unassign it via JavaScript. It just stays hidden.
- AndrewBlemings-Community Member
Thank you, Nathan, this is really helpful. I'd definitely been hoping for a way that didn't require concreting a state for each combination of expressions and poses. I think the sitting-down-talking-on-the-phone pose emoting sixteen expressions as easily as the pointing pose or any other is really multiplicative, and being able to instantly replace either the pose, expression, or perspective of a paper-doll character seems a no-brainer to me for programmatic narrative development.
I mentioned the underscore because I could've sworn I'd recently seen a post where normally-unavailable states could be accessed by prepending them with an underscore. That the expression states exist in the trigger dropdown but not in the API makes me think they're just cordoned off (and if so, for a reason.)
If expressions and poses can be changed via the API and their names are consistent across characters, we just need an abstraction layer. I'm developing a partial prototype that takes something like the below and manipulates objects on the canvas much like visual novel engines do.
let Carl = new Character(object(objID)); // objID maps to Carl's character image
Carl.enters('fromLeft');
Carl.smiles().says('Welcome to this course.');
Carl.waves().says('Follow me!');
For it to scale though I'm thinking would hinge on either a .story template with characters preloaded and fully-mapped (less-than-ideal homebrew) or exposure to also programmatically setting the Character poses and perspectives along with the expressions.
- Nathan_HilliardCommunity Member
Nedim made a post a while back that mentioned some underscored state names as possibilities, although I'm not sure if they actually work (or work anymore). '_default' is a synonym for Normal.
I believe the character graphics are only downloaded to the project if specifically requested at design time. Once downloaded, they are assigned to a state, or the Normal state. Likely the only way to access these is to setup all those you need (i.e., download each) during design.
Another possibility, still tedious but a one and done for all future uses, is to download all of the poses for a character, export them as images, include them into your project as a package (e.g., web object), and then programmatically cycle through them (i.e., swap image sources) as needed according to an established index map, potentially skipping states altogether. I posted some kind of example related to this several months ago.
Related Content
- 8 months ago
- 10 months ago