Skip to content

Per-clip and per-track color #1614

@jminor

Description

@jminor
Collaborator

Feature Request

New Feature

Description

Many NLE programs allow users to color code individual clips and/or tracks in the timeline user interface. This color information is encoded in AAF, and XML files. It would be great if OTIO had a schema property to carry this in a portable way.

Context

Since this PR OpenTimelineIO/raven#14 Raven has minimal support for this as app-specific metadata. That feature was added to help instigate discussion about this schema change.

OTIO already carries per-marker color coding, as an enumerated string value (e.g. "RED", "CYAN", etc.) which has been awkward at times. Perhaps we could revisit that decision and see if maybe storing an RGB triple, or hexadecimal value like "#FF0" might be better.

Activity

jminor

jminor commented on Jun 6, 2023

@jminor
CollaboratorAuthor
jminor

jminor commented on Jun 8, 2023

@jminor
CollaboratorAuthor

Here's a note from @apetrynet (paraphrased from discussion elsewhere):
OTIO could serialize color values as normalized values between 0 and 1 including alpha (like OIIO). That way we're free from bit depth and can convert between known types like hex, string, int etc. This would need some mapping and convenience functions. Also suggest keeping the marker color strings (including lower case representation) as a mapping to normalized values.

e4lam

e4lam commented on Sep 26, 2023

@e4lam

Do we really need to concern ourselves with bit depths? I'd imagine 99% of the use cases would be for user interfaces where HTML codes in the form of #RRGGBBAA (with the AA being optional) hexadecimal codes would cover it.

reinecke

reinecke commented on Oct 4, 2023

@reinecke
Collaborator

I like the proposal @jminor puts forward (with 0-1 float values) because it lets us have one RGBA color schema with utility functions to convert in/out of most the other common representations and it has high reusability for effect parameters or other places where more accurate color may be needed.
The conversion model is in-line with the pattern RationalTime uses with from_frames, to_frames, from_timecode, to_timecode. In this case it might be something like:

  • from_hex
  • to_hex
  • from_rgba_integer_list (Accepts a bit-depth)
  • to_rgba_integer_list (accepts a bit-depth)

For the named colors, we could consider mapping them to specific color by adding a method to this struct. I think it's better to have that struct know about name to color mapping than make a ground truth color representation have some notion of named colors.

semagnum

semagnum commented on May 12, 2025

@semagnum
Contributor

Hi, just wanted to say I coded up a base class working in preparation for Dev Days, I'm hoping to add it as a new attribute for clips and tracks before making a PR.

e4lam

e4lam commented on Aug 5, 2025

@e4lam

@semagnum Sorry, so what is the new format? Can I just do color="#FF0011" now?

semagnum

semagnum commented on Aug 5, 2025

@semagnum
Contributor

This is currently the format: tests/baselines/empty_color.json

I did include utility converters to/from hex code within the Color class, so implementing that would just be a matter of adding it as an option to the deserializer, if that's what you're hoping for :)

e4lam

e4lam commented on Aug 5, 2025

@e4lam

No, I'm seeking clarification on how one is supposed to be able to specify custom colors for tracks/clips/etc that are outside of the preset list of named colors in an OTIO json file. So if I understand what you linked to, one is supposed to create their own color names separately, and then creating your own schema for how to link tracks/clips to the color names? That is exceptionally unwieldy for use cases where you have a large number of tracks/clips each with their separate colours. You have to in essence create your own indexing scheme if that is the case.

jminor

jminor commented on Aug 6, 2025

@jminor
CollaboratorAuthor

You don't need to add colors to a list. Each Clip can have its own color like this:

{
    "OTIO_SCHEMA": "Clip.2",
    "metadata": {},
    "name": "Clip-001",
    "source_range": {
        "OTIO_SCHEMA": "TimeRange.1",
        "duration": {
            "OTIO_SCHEMA": "RationalTime.1",
            "rate": 24.0,
            "value": 3.0
        },
        "start_time": {
            "OTIO_SCHEMA": "RationalTime.1",
            "rate": 24.0,
            "value": 3.0
        }
    },
    "effects": [],
    "markers": [],
    "enabled": true,
    "color": {
        "OTIO_SCHEMA": "Color.1",
        "r": 1.0,
        "g": 0.9,
        "b": 0.8,
        "a": 1.0,
        "name": "something"
    },
    ...
jminor

jminor commented on Aug 6, 2025

@jminor
CollaboratorAuthor

@semagnum in preparing the example above I noticed a bug though... This isn't working as expected:

Python 3.9.12 (main, Apr  5 2022, 01:52:34)
[Clang 12.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import opentimelineio as otio
>>> c = otio.core.Color()
>>> repr(c)
"otio.core.Color(name='', r=1.0, g=1.0, b=1.0, a=1.0)"
>>> s = otio.adapters.write_to_string(c)
>>> print(s)
{
    "OTIO_SCHEMA": "Color.1",
    "r": 1.0,
    "g": 1.0,
    "b": 1.0,
    "a": 1.0,
    "name": ""
}
>>> c2 = otio.adapters.read_from_string(s)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/joshm/.cache/uv/archive-v0/v5vlyU-P9Mp6bsSlMYRsI/lib/python3.9/site-packages/opentimelineio/adapters/__init__.py", line 171, in read_from_string
    return adapter.read_from_string(
  File "/Users/joshm/.cache/uv/archive-v0/v5vlyU-P9Mp6bsSlMYRsI/lib/python3.9/site-packages/opentimelineio/adapters/adapter.py", line 240, in read_from_string
    result = self._execute_function(
  File "/Users/joshm/.cache/uv/archive-v0/v5vlyU-P9Mp6bsSlMYRsI/lib/python3.9/site-packages/opentimelineio/plugins/python_plugin.py", line 144, in _execute_function
    return (getattr(self.module(), func_name)(**kwargs))
  File "/Users/joshm/.cache/uv/archive-v0/v5vlyU-P9Mp6bsSlMYRsI/lib/python3.9/site-packages/opentimelineio/adapters/otio_json.py", line 40, in read_from_string
    return core.deserialize_json_from_string(input_str)
ValueError: type mismatch while decoding: near line 8
>>>
reopened this on Aug 6, 2025
semagnum

semagnum commented on Aug 6, 2025

@semagnum
Contributor

in preparing the example above I noticed a bug though... This isn't working as expected

Gotcha, I'll try to take a look at the deserializer. I'll have more time later this week. Any help troubleshooting would be appreciated.

e4lam

e4lam commented on Aug 6, 2025

@e4lam

You don't need to add colors to a list. Each Clip can have its own color like this:

Ah ok, that sounds a lot better! What is the intention of have the name in that case?

jminor

jminor commented on Aug 6, 2025

@jminor
CollaboratorAuthor

Some applications and formats that OTIO interoperates with only support named colors. Having a name field lets those use cases key off of the name instead of the RGB values. You can see a bit more discussion about that choice here: #1887

semagnum

semagnum commented on Aug 6, 2025

@semagnum
Contributor

I'll try to take a look at the deserializer. I'll have more time later this week. Any help troubleshooting would be appreciated.

Found the root cause, the deserializer was incorrectly looking for a float when the constructor needs a double. Will make a PR.

semagnum

semagnum commented on Aug 11, 2025

@semagnum
Contributor

@jminor if you or someone else could review that PR, I'd appreciate it. I'd hate for a hotfix to linger any longer than it needs to

jminor

jminor commented on Aug 11, 2025

@jminor
CollaboratorAuthor

@semagnum looks great. I verified it locally in a fresh build of Raven. Thanks for the quick fix - merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @jminor@e4lam@reinecke@semagnum

      Issue actions

        Per-clip and per-track color · Issue #1614 · AcademySoftwareFoundation/OpenTimelineIO