> ## Documentation Index
> Fetch the complete documentation index at: https://avala.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Schemas

> Use custom message types with Protobuf, JSON Schema, and ROS message definitions

Beyond the [built-in message types](/docs/visualization/mcap-ros/supported-messages), Avala can parse custom messages defined with Protobuf, JSON Schema, or ROS `.msg` definitions. This means you can record proprietary sensor data or application-specific messages and still visualize them in Avala.

## Protobuf

If your MCAP file uses Protocol Buffers for serialization, include the `.proto` file descriptors in the MCAP schema registry. Avala reads the embedded schema and decodes messages automatically — no server-side schema configuration is needed.

### Embedding Protobuf Schemas

When writing MCAP files with the [MCAP Python library](https://mcap.dev/), register your schema before writing messages:

```python theme={null}
from mcap.writer import Writer

with open("recording.mcap", "wb") as f:
    writer = Writer(f)
    writer.start()

    # Read your compiled .proto file descriptor
    with open("my_message.desc", "rb") as desc:
        schema_data = desc.read()

    schema_id = writer.register_schema(
        name="my_package.MyMessage",
        encoding="protobuf",
        data=schema_data,
    )

    channel_id = writer.register_channel(
        topic="/my_topic",
        message_encoding="protobuf",
        schema_id=schema_id,
    )

    # Write messages using the registered channel
    writer.add_message(
        channel_id=channel_id,
        log_time=timestamp_ns,
        publish_time=timestamp_ns,
        data=my_message.SerializeToString(),
    )

    writer.finish()
```

<Info>
  Generate the `.desc` file from your `.proto` source using `protoc`:

  ```bash theme={null}
  protoc --descriptor_set_out=my_message.desc --include_imports my_message.proto
  ```

  The `--include_imports` flag ensures all referenced types are included in the descriptor.
</Info>

## JSON Schema

MCAP files that use JSON serialization with embedded [JSON Schema](https://json-schema.org/) definitions are parsed automatically. Avala uses the embedded schema to understand field names, types, and structure.

### Embedding JSON Schemas

When writing MCAP files with JSON-encoded messages, register the JSON Schema as the schema data:

```python theme={null}
import json
from mcap.writer import Writer

schema = {
    "type": "object",
    "properties": {
        "timestamp": {"type": "number"},
        "temperature": {"type": "number"},
        "status": {"type": "string"},
    },
}

with open("recording.mcap", "wb") as f:
    writer = Writer(f)
    writer.start()

    schema_id = writer.register_schema(
        name="my_package.SensorReading",
        encoding="jsonschema",
        data=json.dumps(schema).encode(),
    )

    channel_id = writer.register_channel(
        topic="/sensor/temperature",
        message_encoding="json",
        schema_id=schema_id,
    )

    writer.add_message(
        channel_id=channel_id,
        log_time=timestamp_ns,
        publish_time=timestamp_ns,
        data=json.dumps({"timestamp": 1.0, "temperature": 23.5, "status": "ok"}).encode(),
    )

    writer.finish()
```

## ROS Message Definitions

Standard `.msg` files embedded in ROS 1 bags and ROS 2 bags are used for decoding automatically. When you record with `rosbag record` (ROS 1) or `ros2 bag record` (ROS 2), the message definitions are embedded in the bag file by the recording tool.

<Tabs>
  <Tab title="ROS 1">
    ROS 1 bags embed `.msg` definitions in the bag header. No additional steps are needed — `rosbag record` includes them automatically.

    ```bash theme={null}
    # Record with message definitions embedded (default behavior)
    rosbag record /my_custom_topic /camera/image_raw /lidar/points
    ```
  </Tab>

  <Tab title="ROS 2">
    ROS 2 bags store message definitions in the bag metadata. The `ros2 bag record` command includes them by default.

    ```bash theme={null}
    # Record with message definitions embedded (default behavior)
    ros2 bag record /my_custom_topic /camera/image_raw /lidar/points
    ```
  </Tab>
</Tabs>

If you convert ROS bags to MCAP, make sure the conversion tool preserves the embedded message definitions. The [mcap CLI](https://mcap.dev/guides/cli) handles this automatically:

```bash theme={null}
# Convert a ROS 1 bag to MCAP (preserves message definitions)
mcap convert recording.bag recording.mcap

# Convert a ROS 2 bag to MCAP
mcap convert recording.db3 recording.mcap
```

## How Custom Messages Are Displayed

Custom messages that do not match a [built-in schema](/docs/visualization/mcap-ros/supported-messages) are displayed in the **Raw Messages panel** by default. The panel renders every field as an expandable JSON tree, making it straightforward to inspect the data.

In addition:

* **Numeric fields** within custom messages can be plotted in the Plot panel. If a custom message contains fields like `temperature`, `velocity`, or any numeric value, you can assign the topic to a Plot panel to see those values over time.
* **Known sub-message types** are recognized even within custom messages. For example, if your custom message contains a `sensor_msgs/PointCloud2` field, Avala can render that field in a 3D panel.

<Tip>
  Need a fully custom visualization for your proprietary message type? Contact [support@avala.ai](mailto:support@avala.ai) to discuss custom panel rendering for your use case.
</Tip>

## Best Practices

<CardGroup cols={2}>
  <Card title="" icon="file-code">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Embed schemas in the file</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Always include schema definitions (Protobuf descriptors, JSON Schemas, or ROS .msg files) directly in the MCAP file. This ensures Avala can decode every message without external dependencies.</p>
  </Card>

  <Card title="" icon="tags">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Use consistent naming</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Name your custom schemas with a clear package prefix (e.g., `my_company.SensorReading`). Consistent naming makes it easier to identify topics in the viewer and across recordings.</p>
  </Card>

  <Card title="" icon="circle-info">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Include type information</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Provide full type annotations in your schemas. Typed fields enable richer visualization — for example, Avala can plot numeric fields and display string fields as labels.</p>
  </Card>

  <Card title="" icon="vial">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Test with a small recording</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Before uploading a large dataset, test your custom schema with a short recording to verify that Avala parses and displays the messages correctly.</p>
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="" icon="list-check" href="/docs/visualization/mcap-ros/supported-messages">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Supported Messages</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>See the full list of built-in message types that Avala recognizes automatically.</p>
  </Card>

  <Card title="" icon="table-cells-large" href="/docs/visualization/mcap-ros/panels">
    <p style={{fontWeight: 600, fontSize: '18px', marginBottom: '4px', marginTop: '8px', color: 'inherit'}}>Panel Types</p>
    <p style={{fontSize: '14px', marginTop: '0px', opacity: 0.6}}>Learn how topics are assigned to panel types and how to override the defaults.</p>
  </Card>
</CardGroup>
