This library allows you to parse, manipulate and save files in the BVH motion capture format.

To get started, remember to add the obligatory “require” statements:

require 'rubygems'
require 'bvh'

Once done, you can load a BVH file pretty easily:

bvh = Bvh.import("path/to/animation.bvh")

And you can export them just so:


For a more exhaustive set of examples, see EXAMPLES.


Although BVH is marked as 1.0, there is some functionality I haven't yet tested. In particular, the transform matrices may or may not be accurate. The numbers look right from my command line, but I haven't had a chance to plug BVH into my graphics engine, so they may in fact be wrong. Please let me know what you learn if you've tested this!

The Format

What follows is a run-down of what I've learned about the BVH file format while researching and coding this library. I reserve the right to be mistaken (but I don't think I am).

The BVH format was developed by a now-defunct company called Biovision, and stands for Biovision Hierarchical data format. Used to store motion capture data, a BVH file consists of two sections: a HIERARCHY section, which describes the skeletal structure that the motion capture data pertains to; and a MOTION section, which stores the motion capture data itself.


The HIERARCHY section contains a set of nested nodes which describe a character. Most of these nodes are named. Though the name of a given node seems to be rarely used within the context of the BVH file itself, I suspect it will be quite useful when biping 3D models to various body parts – but that's well beyond the scope of this library.

Each node name is expected to be unique, regardless of usage, and this library enforces that. Every node in the HIERARCHY section is referred to by this library as a Bone, and a collection of Bones (beginning with the ROOT node) is referred to as a Skeleton.

The first (outermost) node is tagged “ROOT” and is, obviously, considered the root of the skeleton. (Technically, there may be more than one ROOT in a BVH file, but this is atypical. This library does support multiple roots, however.) Following the tag is the node name, and on the next line is an opening curly brace (“{”).

Information about the current Bone follows the curly braces, and includes OFFSET and CHANNELS data. OFFSET information specifies the offset from the parent Bone, or an offset from the origin for a root. Offset information of joints are also used to infer length and orientation for their parent bone. If multiple joints are found, the length and orientation are determined from the first of them.

By convention, the data for a given node is preceded by a tab character for readability. This is not technically necessary, but some stricter BVH parsers require this explicitly and cannot support other forms of white space. This library supports arbitrary amounts and types of white space.

Child nodes, which are tagged “JOINT”, follow the OFFSET and CHANNELS data, and recursively define the tree until an “End Site” node is detected. The “End Site” node contains only OFFSET information, which is used exclusively to infer length and orientation of the last node in the list.

The CHANNELS information for each node specifies the order in which the channel information will appear; this applies to OFFSET data (even though the Xposition, Yposition and Zposition channels are generally only declared at the root level, and generally follow in the expected order) as well as the frame data in the MOTION segment.


The Motion section of the file contains the motion capture data itself. There are generally 2 lines preceding the data: “Frames” and “Frame Time”. The “Frames” line specifies the number of frames that will follow, and the “Frame Time” field indicates how many seconds will pass per frame. Most BVH files specify the Frame Time as 0.0333333, or 30 frames per second, but this can vary.

This BVH library dynamically counts the number of frames, and only uses the “Frames” field to verify that all available data has been loaded. If the “Frames” field is missing, this library will not raise an error – it will simply be unable to verify the number of frames after a file has been loaded. (An error will be raised if the “Frames” field is supplied and the numbers do not match, however.)

Finally, the remainder of the file consists of numbers delimited by a tab character and a space (though, again, this library isn't particularly picky about the white space). Each line constitutes a single frame, and the series of numbers on each line apply to the CHANNELS information in the order it was seen in the file. Because the same order is maintained, the first several numbers usually correspond to translation (usually X, Y and Z), while the remainder of the numbers correspond to rotation.

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don't break it in a future version unintentionally.

  • Commit – do not mess with rakefile, version, or history. (If you want to have your own version, that's fine, but bump version in a commit by itself so that I can ignore it when I pull.)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2009 Colin MacKenzie IV. See LICENSE for details.