YAML is an object-based data format with a flexible structure. Its specification includes a huge array of wacky abilities, but we only use a few of them.

Here are the most important YAML features to know about when writing Tangram scene files.

mappings

Tangram makes heavy use of the YAML structures known as "mappings," known elsewhere as "associative arrays" or "dictionaries". They are made up of "key/value pairs" – in these docs, we usually call these parameters, because that's how we use them:

parameter: value

When nested, YAML calls this a "collection" – we usually call this an element with a number of parameters:

element:
    parameter1: value
    parameter2: value

By definition, a "mapping" is an orderless collection of objects. For this reason, at a given level within a given object, parameter names must be unique:

element:
    parameter1: value
    parameter1: value # not allowed, parameter name is repeated

Conflicts may be resolved differently at different times, and you can't rely on document order to resolve them:

# only one camera can be active at a time – here, you can't be sure which one it will be.
camera1:
    active: true
camera2:
    active: true

Finally, a parameter can't also be an element:

# THIS WON'T WORK
parameter1: value1
    subparameter1: value2

This is because the value of "parameter1" can't be both "value1" and an object containing other key/value pairs – it has to be one or the other.

In this documentation, we refer to both parameters and elements as "objects".

Note: YAML key names can't start with a number.

sources:
    1860: # THIS WON'T WORK

object syntax

YAML supports two kinds of syntax when writing nested objects: block syntax and flow syntax.

block syntax

Block syntax requires each level of an object to be indented with spaces – any number of spaces or tabs is allowed, as long as it's consistent throughout the file. It is relatively easy to read, though it tends to result in longer files.

element:
    subelement:
        parameter1: value1
        parameter2: value2
    subelement2:
        parameter1: value1
        parameter2: value2

Note: if you mix spaces and tabs, the parser will throw an error like this one:

YAMLException {name: "YAMLException", reason: "bad indentation of a mapping entry"}

flow syntax

Here is the same object as the first example above, written in the more compact flow syntax:

element1: { subelement1: { parameter1: value1, parameter2: value2 }, subelement2: { parameter1: value1, parameter2: value2 } }

In some cases, this syntax may be harder to read, but it's usually fine for shorter objects, or patterns which are repeated frequently:

roads:
    data: { source: local, filter: roads }

Syntax examples

For further examples, check out our many fine demos!

lists

Lists are written differently in each of the above syntax styles.

block lists

element:
    parameter:
        - item 1
        - item 2
        - item 3

flow lists

element: { parameter: [ item1, item2, item3 ] }

Note that lists and mappings are interpreted differently in various situations.

See also: Lists Imply Any, Mappings Imply All

syntax mixing

Block syntax can enclose flow syntax, but not the other way around – once you start an object in flow syntax, you have to finish it before you can move back into block syntax.

element:
    parameter: { [ item1, item2, item3 ] }

element:
    parameter:
        - item1
        - item2: { parameter1: value1, parameter2: value2 }
        - item3

# THIS WILL NOT WORK
element: { parameter:
    - item1
    - item2
    - item3

data types

Tangram's data types are based on YAML's functionality, but we've extended them a bit in certain contexts.

duck typing

YAML interprets data types automatically:

5 # that's an int
5.2 # that's a float
duck # obviously a string
5.2 ducks # mixed-type => string
[1.0, .5, .75] # array of floats (aka a parade)

stops

Our "stops" data structure is a way to define a relationship between two ranges of values. It is defined as an array of two-item arrays, like so:

[[12, 3], [14, 6], [16, 9]]

The first value in each pair is always a zoom level. The second value in each pair is interpreted in the context of the current parameter. Stops may be used with all color and distance parameters, including width, focal_length, fov, and z.

For instance, in a width block, if no units are specified, each pair is interpreted as [zoom, meters], because meters is the default unit of width. The above example will define a value of 3m at zoom 12, 6m at zoom 14, and 9m at zoom 16.

At intermediate zoom levels, values will be interpolated linearly, with behavior depending on draw style and parameter. In the above example, at zoom 13, the value will be 4.5m, and at zoom 13.5, the value will be 5.25m. However, for all color values, the values are only be updated when tile geometry is built – typically at whole-number zoom changes.

Outside of the range specified by the stops, the values are capped by the highest and lowest values in the range – so in the above example, the value at zoom 9 is also 3m, and the value at zoom 18 is still 9m.

color: [[10, [0.3, 0.4, 0.3]], [14, [0.5, 0.825, 0.5]]]
width: [[13, 0px], [14, 3px], [16, 5px], [18, 10px]]

value types

Tangram accepts a variety of values and units depending on the parameter.

When scalar values with units are specified in the scene file, Tangram accepts px for pixels and m for Mercator meters, which are a typical unit of measurement on a web map. (Mercator meters have the same apparent length anywhere on a Mercator-projected map, whereas an actual meter's apparent length on a Mercator map varies with distance from the equator. A Mercator meter and a real meter are equal in length at the equator.)

Colors may be specified in a variety of ways, including as GLSL-style arrays of values from 0-1:

- RGB array: `[.5, 0., 1.]`
- RGBA array: `[.5, 0., 1.]`

As well as any of the CSS specification types:

- Named colors: `red`, `blue`, `salmon`
- Hex#: `"#fff"`, `"#000"`, `"#9CE6E5"`
- RGB: `rgb(255, 190, 0)`
- RGBA#*: `rgb(255, 190, 0, .5)`
- HSL: `hsl(180, 100%, 100%)`
- HSL#*: `hsla(180, 100%, 100%, 50%)`

Hex values must be in quotes, to prevent interpretation as a YAML comment. *Currently, alpha values are ignored in the add and multiply blend modes, and respected in the inlay and overlay modes. For more on this, see the blend entry.

reserved keywords

Our YAML parser detects certain keywords contextually based on the element or parameter in which they are used. In these contexts, these words are reserved keywords and can't be used as custom element names.

For example, the layers element has three possible parameters: data, filter, and draw. It also allows custom sub-layers, which can be named anything except "data", "filter", or "draw".

function

Strings starting with function will be passed to the style builder as JavaScript in certain contexts, including: color, width, order, interactive, visible, filter, size, fill, and stroke. These functions will be evaluated at runtime, and the returned value will be passed to the parameter.

# Single-line JavaScript example:
width: function () { return 2.5 * Math.log($zoom); }

Note that functions cannot return a unit type – so in cases where a function is specifying a width, the default unit of meters will be assumed. To return pixel values, use the $meters_per_pixel keyword.

$geometry

The $geometry keyword can specify a filter to match a specific type of geometry, for cases when a FeatureCollection includes multiple geometry types.

Allowable values for this filter are:

  • polygon
  • point
  • line
labels:
   filter: { $geometry: point }

See keyword properties.

$layer

The $layer keyword can specify a feature filter to match a feature from a given named layer in the datasource, for cases when a Tangram layer's source includes multiple layers.

data: { source: osm, layer: [buildings, pois] }
buildings:
    filter: { $layer: buildings }

See keyword properties.

$meters_per_pixel

The $meters_per_pixel keyword is equal to the number of meters equivalent to a single pixel at the current zoom level. It may be used in draw style functions, which assume that all returned values specifying a length or size are in meters. This keyword can therefore be used to return an amount in pixels.

width: function() { return $zoom / 4. * $meters_per_pixel; }

See keyword properties.

$zoom

The $zoom keyword may be used to define filters with optional min and max parameters.

outline:
   filter: { $zoom: { min: 15, max: 20 } }

See keyword properties.

multi-line strings

One of the reasons we chose YAML as our scene file format is its ability to handle multi-line strings with a minimum of fuss. In block syntax only, start an parameter's value with a "pipe" character (|) followed by a newline, and everything that isn't indented less after that will be treated as a single string value, newlines included:

element:
    parameter: |
        This is my multi-line string.
        There are many like it,
        But this one is mine.

        It even has an empty line!
        Sublime.

                Indents don't matter
              As long as they don't
            start
          before
        here.
                     Clear?

This lets us put code straight into an attribute value, and it still looks like code:

# Multi-line JavaScript example:
style:
    color: [0.5, 0.5, 0.5]
    width: |
        function () {
          return 2.5 * Math.log(zoom);
        }

# GLSL Example:
elevator:
    base: polygons
    animated: true
    shaders:
        blocks:
            position: |
                // Elevator buildings
                if (position.z > 0.01) {
                    position.z *= (sin(position.z + u_time) + 1.0);
                }

comments

# This is a YAML comment.
# There is no way to write block comments in YAML, sorry.

# To quote http://stackoverflow.com/questions/2276572/how-do-you-do-block-comment-in-yaml:

# If you want to write
# a block-commented Haiku
# you'll need three pound signs

Bear in mind that in multi-line strings, pound signs lose their ability to comment code! You'll have to use the comment convention in whatever language you're writing. (This can be a pain when using auto-commenting in text editors.)

url

url attributes may be used to link to external YAML files in the styles element, as well as in the shader block elements global, color, normal, and position. This allows for more modular shader construction as well as easy sharing of styles.

linked styles

dots:
    url: styles/dots.yaml

When the url parameter is used in the styles block, the linked .yaml file should include a replica of the entire style element as it would appear in the scene file, including the style name:

# styles/dots.yaml
dots:
    base: polygons
    shaders:
        uniforms:
            ...

linked shader blocks

shaders:
    blocks:
        global:
            url: functions/hsv2rgb.glsl

When the url attribute is used in place of any blocks parameters, the linked .glsl file should only include GLSL code, as it would appear in the parameter's value:

// hsv2rgb.glsl
vec3 hsv2rgb(vec3 c) {
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

JSON compatibility

YAML's capabilities are officially a superset of JSON, which makes conversion between the two formats a cinch.