You're seeing channels and samplers there, not individual keyframes. In this case, one animation is simultaneously targeting the translation, rotation, and scale channels of the same node, with three separate "samplers" (sets of keyframes per channel).
Not shown above, your glTF file has a section called accessors
that tells viewers how to slice out portions of binary data from an associated .bin
file. The .bin
file contains mostly mesh position/normal/UV data, but also contains the keyframe times and values.
So in the above snippet, that first channel targets the translation
of node 3 with sampler 0 (the first sampler). The sampler there asks for LINEAR
interpolation of keyframes from animation times (input: 8
) for translation keyframe values (output: 9
). In the accessors
section, accessors 8 and 9 (using zero-based indexing) are references to the keyframe times (in 8) and values (in 9). They are separate because the data type of the output depends on what's being targeted (rotations are vec4 quaternions for example, while translations and scales are vec3, and the input times are just floats).
The accessors
in question (8 and 9 for example) will contain a value "count"
that effectively tells you the number of keyframes. If you have 5 keyframes for example, accessor 8 should have "count": 5
keyframe times, and accessor 9 will also have "count": 5
keyframe values. The glTF Validator will flag an error if these counts don't match.