The <spine-widget> tag allows you to place your Spine animations into a web page.

By default, the animation bounds are calculated using the given animation, or the setup pose if no animation is provided.
The bounds is centered and scaled to fit the parent container.

                
            
You can change the fit mode of your Spine animation using the fit attribute.

This is fit="fill". Default fit value is fit="contain".
If you want to preserve the original scale, you can use the fit="none". In combination with that, you can use the scale attribute to choose you desired scale.

Other fit modes are width, width, height, cover,and scaleDown.

                
            
If you want to manually size the Spine widget, specify the attributes width and height in pixels (without the px unit).

If you prefer you can style the component using the style attribute. There you have more styling options.

                
            
Mode origin center the animation world origin with the center of the HTML element.
You are responsible to scale the skeleton using this mode.

Move the origin by a percentage of the div width and height by using x-axis and y-axis respectively.

                
            
Use offset-x and offset-y to move you skeleton left or right by the pixel amount you specify.

                
            
You can virtually add a padding to the element container by using pad-left, pad-right, pad-top, pad-bottom.

As a value you can use a percentage of the width for left and right, and of the height for top and bottom.

                
            
You can customize the bounds, for example to focus on certain details of your animation.

The bounds-x, bounds-y, bounds-width and bounds-height allows to define custom bounds.

In this example we're zooming in into Celeste's face. You probably want to use clip in this case to avoid the skeleton overflow.

                
            
Give an identifier to your widget to get it by using the spine.getSpineWidget function. You can easily access the Skeleton and the AnimationState of your character, and use them as if you were using spine-webgl.

If you change animation, you can ask the widget to scale the skeleton based on the new animation.

                
            

To change animation, we could also just change the animation attribute. The widget will reinitiate itself and change animation.

In this case you would use auto-recalculate-bounds to ask the widget to always recalculate the bounds, as in the top example.

If want to keep the scale consistent, but fit multiple animations in the container, you can use the animation-bounds attribute to define a bounds containing a list of animations, as in the bottom example.


                
            

If you want to display a sequence of animations without using js or on multiple tracks, you can use the animations attribute.

It accepts a string composed of groups surrounded by square brackets, like this: [...][...][...]

Each square bracket represents an animation to play, with some parameters. It contains a comma separated list with the following:

  1. track: the number of the track on which to play the animation
  2. animation name: the name of the animation to play
  3. loop: true, if this animation has to loop. False, otherwise
  4. delay: the seconds to wait after the start of the previous animation, to play the animatino of this group (not available for the first animation on this track)
  5. mixDuration: the mix duration between this animation and the previous (not available for the first animation on this track)

Once you composed your animation, if you that is loops once it reaches the end, you can add the special group [loop, trackNumber], where:

  • loop: is the "loop" string to identify this special group
  • trackNumber: is the number of the track you want to be looped

The parameters of the first group of each track are passed to the `setAnimation` method, while the remaining groups on the track use `addAnimation`.

You can use respectively use `setEmptyAnimation` or `addEmptyAnimation`, by using the string #EMPTY# as animation name. In this case the loop parameter is ignored.

The default-mix attribute allow to the the default mix of the AnimationState.

Have a look at the two examples below.

Spineboy here uses the following value for animations attribute.

We use a single track for this animation. Let's analyze it:
  1. [loop, 0]: when the track 0 reaches the end, start from the beginning
  2. [0, idle, true]: set the idle animation in loop
  3. [0, run, true, 2, 0.25]: queue a cycle of the run animation, start it after 2 seconds from the beginning of the previous one, set a mix of 0.25 seconds from the previous one.
  4. [0, run, false]: queue a cycle of run animation
  5. [0, run, false]: queue a cycle of run animation
  6. [0, run-to-idle, false, 0, 0.15]: queue a cycle of run-to-idle animation, with no delay, and a mix of 0.15 seconds
  7. [0, idle, true]: queue the idle animation in loop
  8. [0, jump, false, 0, 0.15]: queue a cycle of jump animation in loop, with no delay, and a mix of 0.15 seconds
  9. [0, walk, false, 0, 0.05]: queue a cycle of walk animation in loop, with no delay, and a mix of 0.05 seconds
  10. [0, death, false, 0, 0.05]: queue a cycle of death animation in loop, with no delay, and a mix of 0.05 seconds

Celeste here uses the following value for animations attribute.

It uses two tracks. In track 0 we simply set the wings-and-feet animation.
In track 1 we loop over the entire animation, set an empty animation and queue an eyeblink animation with a 2 seconds delay.

You can modify the textarea above and experiment with the values. For example, change the delay from 2 to 0.5, or add the swing animation to track 0 like this [0, swing, true, 5, .5] with a delay of 5 seconds and a mix of 0.5 seconds. Click the button below and Celeste will start to blink the eyes more frequently.


                
            
Moving the div will move the skeleton origin.
Resizing the div will resize the skeleton in inside mode, but not in origin mode.
Interact with the example above dragging the div and resizing it

                
            
You can view the skeleton world origin (green), the root bone position (red), and the bounds rectangle and center (blue) by setting debug to true.

                
            
If you want to embed your assets within the page, you can inline them using their base64 version. Use a stringified json object containing as keys the assets name and as values their base64 version.

                
            
A loading spinner is shown during assets loading. Click the button below to simulate a 2 seconds loading:



If you do not want to show the loading spinner, set spinner="false".
Click the button below to toggle the spinner.


                
            
It's super easy to show your different skins and animations. Just make a table and use the skin and animation attributes.

                
            
If you have many atlas pages, for example one for each skin, and you want to show only some of the skins, pass to the pages the atlas pages you want to load as a comma concatenated list of indices.

                
            
Let's do the same thing above, but programmatically! Create two arrays, one for the skin and the other for the animations, and loop over them.

spine.createSpineWidget allows you to create a spine widget.

By default, assets are loaded immeaditely. You can postpone that by setting manual-start="false". Then add the widget to the dom using the asynchronous method appendTo. It's your responsibility to call start() on the widget. As usual, just wait on the loadingPromise to act on the skeleton or the state.

                
            
When the widget (or the parent element) enters in the viewport, the callback onScreenFunction is invoked.

By default, the callback does two things:
  • set onScreenAtLeastOnce to true when the widget enters the viewport the first time
  • if manual-start and on-screen-manual-start are set the widget start is invoked the first time the widget enters the viewport and the assets are loaded only in that moment.

The assets of the coin below are loaded only when the widget enters the viewport.

You can overwrite the onScreenFunction behaviour. For example, the raptor below changes animation everytime the widget enters the viewport.

                
            
If you want to load textures programmatically, you can just pass as pages to load an empty value liek this pages="".

In this way the skeleton and the atlas are loaded, but not the textures.
Then you can loads the textures whenever you want.










                
            
Widgets are not rendered while they are off screen.

The state and skeleton update, and the skeleton apply and the skeleton updateWorldTransform functions are not invoked when the widget is off screen.

If you want the update functions to be invoked in any case, set offscreen=update.
If you want all the functions to be invoked in any case, set offscreen=pose.

You can also overwrite the update function. Just assign a function to the update property of the widget. In that it's your responsibility to skip the update/apply. You can use the onScreen property for convinience.

                
            
If for some reason your skeleton bounds go outside the div, you can use the clip property to clip everything is outside the html container.

Be aware that this will break batching!

                
            
More examples for clip attribute.

                
            
If you use a spine widget in a element that has an ancestor that does not follow the webpage scroll, the effect might not be the desired one. You might encounter these problems:

1) For scrollable containers, the widget will be slightly slower to scroll than the html behind. The effect is more evident for lower refresh rate display.
2) For scrollable containers, the widget will overflow the container bounds until the widget html element container is visible
3) For fixed containers, the widget will scroll in a jerky way

In order to fix this behaviour, it is necessary to insert a dedicated spine-overlay webcomponent as a direct child of the container. Moreover, it is necessary to perform the following actions:

1) The scrollable container must have a transform css attribute. If it hasn't this attribute the spine-overlay will add it for you. If your scrollable container has already this css attribute, or if you prefer to add it by yourself (example: transform: translateZ(0);), set the scrollable-tweak-off to the spine-overlay.
2) The spine-overlay must have the scrollable attribute
3) The spine-overlay must have an overlay-id attribute. Choose the value you prefer.
4) Each spine-widget must have an overlay-id attribute. The same as the hosting spine-overlay.

Additionally, you can set overflow-top, overflow-bottom, overflow-left, overflow-right attributes to the spine-overlay in order to make the canvas bigger and prevent scrolling artifacts.

See the two examples below:
- Click the following button to open two elements with fixed positioned. The left one does not have a dedicated overlay and will scroll in a jerky way.
- Below there are two scrolling items. The left one does not have a dedicated overlay, it will lag on scroll and the widgets will overflow the container.


                
            
As a bonus item, you can move you skeleton around just by setting the isdraggable property to true.

                
            
If you need to determine the cursor position in the overlay world, you might find useful the following properties.
For spine-widget: For spine-overlay: You can use these property to interact with your widget. See the following examples where the owl eyes will follow the cursor, even if you drag the owls in another position. Exaggerate the movement by deselection the checkbox below.



This feature is experimental and might be removed in the future.

                
            
You can attach callback to your widget to react at pointer interactions. Just make it isinteractive.

You can attach a callback for interactions with the widget bounds or with slots. The available events are down, up, enter, leave, move, and drag.

In the following example, if the cursor enters the bounds, the jump animation is set, while the wave animation is set when the cursor leaves.
If you click on the head-base slot (the face), you can change the normal and dark tint with the colors selected in the two following selectors.

Tint normal:

Tint black:


                
            
You can make your HTMLElements follow slots. This feature is convenient when you need to generate dynamic text or content that integrates with your animation.

Invoke the `followSlot` function that takes as input:

  1. The Slot or the slot name to follow
  2. The HTMLElement that follows the slot
  3. An object with the following options:
    • followOpacity: the element opacity is connected to the slot alpha
    • followScale: the element scale is connected to the slot scale
    • followRotation: the element rotation is connected to the slot rotation
    • followAttachmentAttach: the element is shown/hidden depending if the slots contains an attachment or not
    • hideAttachment: the slot attachment is hidden as if the element replaced the attachment


                
            
`followSlot` works even with other spine widgets! It works even if you drag it :D

                
            
A login UI made using the chibi stickers and a button made using Spine.

The chibi sticker does the following:

  • It looks at the cursor when no input field is selected
  • Look at the caret when username input field is selected
  • Cover its eyes when password input field is selected
  • React in two different ways depending on the password

The button does the following:

  • Starts some animation when cursor enters, leaves, stays, or click the button
  • Appends a div containing the LOGIN text to a slot
  • Submits the form on click

Password is password