Some Thoughts
I used to work at the PGA TOUR. I built tools there to automate production of player head shots and spent a bunch of time working with sponsor logos. I can offer a few things to consider based on that work:
Use image pixel dimensions instead of aspect ratios.
As a downstream consumer, it’s more important for me to know the size of the image compared to the aspect ratio when making decisions about how to use it.
To use extreme examples, a 1 pixel by 1 pixel image has a 1:1 aspect ratio, but it’s useless for general display. If an image is ten million pixels by ten million pixels it’s also 1:1. But, I’d want to know how big it is before I tried to download it.
Without the pixel information, apps not only have to download images to determine their sizes, they also need tools to figure out the dimensions. Native JavaScript in standard browsers can do this. If you’re building a server side app (e.g. in rust or whatever) you’d likely have to install extra libraries to get at the data. That’s an unnecessary lift compared just doing the math on a pair of width/height dimensions to calculate the ratio when its needed.
(In theory, the aspect ratio could be included along with the pixel dimensions in the data. But, it’s a calculated value. I prefer to keep those out to avoid a mismatch if there’s a bug generating the value.)
Provide an array of different versions (assets) for each record.
This is about more than just making smaller thumbnails of larger images. It also provides an opportunity for art direction.
Take head shots as an example. Two different sizes of the same head shot may have different cropping requirements. For instance, larger images may push the person to one side so text overlays can be added to the other. Doing an automatic, center crop of the image to make a profile pic results in a face that’s half cut off. If multiple versions are available for different sizes then maintainers can provide appropriate cropped versions for each use case.
Logos are another example. Lots of the ones I worked with included text that only took up a small portion of the overall image canvas. While things looked fine at large sizes, the text became unreadable as the images got smaller. Similar to the head shots, maintainers of the image can make explicit versions for different sizes. This can go even farther and switch out source entirely (e.g. a logo with full name of a company at large sizes and a mark with just the first letter when it’s smaller).
As someone using the images, I don’t want to have to create logic on my side determine which crop or instance version of an image I should use at a given size. I just want to call “Example Logo”, get the list of available sizes and pull the one that fits the size I need.
Of course, there’s no requirement to add extra image assets. And, there’s no requirement that if you do they be anything other than lower resolution of the source. (Setting them up that way would make them easy to use with srcset and sizes.
Include a MIME Content-Type for each asset.
i.e. don’t rely on names or keys to determine the image type.
Each record has two keys to address it by name.
Each can be any string. The idea is to mimic a file system where the first key can stand in as a file name (this key is required), and the second key can represent a folder structure (this key is optional). The approach lends it self to doing things like grabbing all files with the same directory style key in the same way you could grab a group of images in a folder.
Alt text should be attached to each asset as an array.
The array should be required but it can be empty.
Alt text items in the array should contain the ISO 639 code the language in the text.
Apps can chose what language they want to use.
An extended alt text field should be included to match the format of the IPTC accessibility fields.
IPTC link
The IPTC limits the size of the first field to something like a couple hundred characters. The lexicon shouldn’t do that as it forces another decision point about what to write where. The primary purpose of the second field in the lexicon is simply to avoid having to alter alt tags coming in from IPTC sources.
Include an updatedAt field so apps can cache and check to see if they need to re-fetch.
The field should always be populated (when an entry is first created it’s the same value as the createdAt field). Doing this makes it easier to consume because apps can always look in the same place for the data, or use them independently if they want.
Add tags at the top level of the record as an array of strings.
This provides a way for folks/app to categorize/filter their collections.
Add tags at the individual asset level as an array of strings.
The idea here is to let folks do things like tag individual assets for the customized purposes. The goal isn’t to create any type of universal standard. Merely to provide a way for maintainers and consumers to communicate intent in agreed manners.
Add copyright status, notice, and info url to each asset.
I’m putting this on the individual assets as there are cases where there my be different copyright for assets.
The three fields I’ve included “infoURL”, “notice”, and “status” are based of the default fields used for file info in photoshop. Will definitely need help figuring out the specifics of what should be in the slot.
Have a reference link to more metadata (e.g. IPTC, or XMP, or whatever. )
Those links should be at the asset level so that individual assets can each reference their own metadata records which may differ if the image content differs. (My thinking here is that individual lexicons would be defined for the various image metadata standards. Or, maybe we take a shot an defining The One True (extensible) Metadata lexicon since we’re working in a new environment. I’d probably go with mapping the existing standards though).
I’m not sure yet how to link to another record. So, this isn’t reflected in the prototype lexicon.
Prototype
I took a first stab at making a lexicon based on all that. I tried to line it up with ing.dasl.masl, but I don’t have it linked/referenced in the lexicon. This is also my first time messing with lexicon work. I won’t be surprised to learn parts are out of whack. That’s fine. This is more about having a starting point to work off of.
{
"id": "this-is-a-placeholder",
"description": "A collection of associated assets to be delivered in response to calls for an image and corresponding metadata.",
"defs": {
"main": {
"key": "tid",
"type": "record",
"record": {
"type": "object",
"required": [
"cid",
"assets",
"createdAt",
"nameKey",
"tags",
"updatedAt"
],
"properties": {
"assets": {
"items": {
"ref": "#asset",
"type": "ref"
},
"type": "array"
},
"cid": {
"description": "The DRISL CID of the MASL for the image",
"format": "cid",
"type": "string"
},
"createdAt": {
"description": "The timestamp that represents when the record was created.",
"format": "datetime",
"type": "string"
},
"nameKey": {
"description": "The primary human readable key for the record. It can be used to represent the file name. (File extensions should not be included. Types are provided by the contentType",
"type": "string"
},
"pathKey": {
"description": "A optional, secondary human readable key. It can contain slashes and be used to represent a storage directory path.",
"type": "string"
},
"tags": {
"items": {
"type": "string"
},
"type": "array"
},
"updatedAt": {
"description": "The timestamp that represents the last time the record was update. Always availalbe. Matches createdAt when the image is first created.",
"format": "datetime",
"type": "string"
}
}
}
}
},
"alt": {
"type": "record",
"record": {
"required": [
"extendedText",
"language",
"text"
],
"properties": {
"extendedText": {
"type": "string",
"description": "A second text field to match the format of the IPTC metadata setup."
},
"language": {
"type": "string",
"description": "The ISO 639 language code for the alt text."
},
"text": {
"type": "string",
"description": "The text content of the alt text."
}
}
}
},
"asset": {
"type": "object",
"required": [
"alts",
"contentType",
"copyright",
"height",
"payload",
"tags",
"width"
],
"properties": {
"alts": {
"type": "array",
"items": {
"ref": "#alt",
"type": "ref"
}
},
"contentType": {
"description": "The MIME content type for the payload",
"type": "string"
},
"copyright": {
"ref": "#copyright",
"type": "ref"
},
"height": {
"description": "The height of the image payload in pixels",
"type": "integer"
},
"payload": {
"_TODO": "Verify SVG isn't under 'image/*' and decided if it should be accepted or not",
"accept": [
"image/*"
],
"description": "The raw image blob",
"type": "blob"
},
"tags": {
"items": {
"type": "string"
},
"type": "array"
},
"width": {
"description": "The width of the image payload in pixels",
"type": "integer"
}
}
},
"copyright": {
"type": "object",
"required": [
"infoURL",
"notice",
"status"
],
"properties": {
"infoURL": {
"description": "The link to the place with more information about the copyright.",
"type": "string"
},
"notice": {
"description": "The text to use when display the copyright",
"type": "string"
},
"status": {
"description": "The copyright status of the asset",
"type": "string",
"enum": ["unknown", "publicDomain", "copyrighted"]
}
}
},
"lexicon": 1,
"$type": "com.atproto.lexicon.schema"
}
Outro
Obviously, a little more scope than the original, but this is a draft of what I’d like to see in something I use that makes it as easy as possible for me to just send a request, get a list of sizes, and choose the on that works with no additional work.
In terms of getting behind the effort, I’d be happy to help lead the work. This kinda stuff is right up my alley. I’m guessing there’s guidance out there about what to do for next steps. If anyone’s got links, I’d appreciate the pointers.
P.S. is it cool to ping people here? essentialrandom_bsky_social and trezy_codes were discussing images last night on stream.place. This might be of interest to them.
UPDATE: Just fond this which has notes on community lexicon dev. Looking at it next: https://blog.lexicon.community/