Proposal: a community lexicon for standalone images

The need

I made this image. It is mine, and I am on the Atmosphere — I should be able to deploy it in N places as I go.

The live case: I run EPTSS, a song-cover community that submits tracks through plyr.fm, and I build Luminframe, an atproto image editor. My users make art in Luminframe and publish music on plyr — their art should just be there as track artwork. I PR’d plyr to make that real, and plyr had to hard-code com.luminframe.image. Every reader learning every writer by name doesn’t scale; one shared collection does — n writers, readers wire up once. What standard.site did for long-form.

Smaller than standard.site

standard.site standardizes distribution — publications, subscriptions, a text fallback over many content formats. Images need less: the blob is the universal format — content, wrapper, and fallback at once. This is just the artifact layer that distribution can later point at.

Prior art

com.luminframe.image and social.grain.photo already write nearly this record independently. app.bsky.embed.images is embed-only. lexicon.community has no media namespace yet.

Draft schema

Provisionally community.lexicon.media.image — the name is yours, the shape is what I’ll defend. Full JSON attached.

field req why
image (blob) yes the thing itself — content and fallback
aspectRatio yes layout before bytes arrive
createdAt yes natural ordering
alt no* the image’s voice when eyes can’t see it

*optional in schema, expected in practice.

Absent by design: titles, galleries, EXIF, location. A field gets in when two independent implementers need it.

Extensions and adoption

Records are open — Luminframe’s standard records also carry its edit recipe; generic readers ignore it. One record, one center. And since a blob can back multiple records in one repo, dual-publishing alongside your existing lexicon is zero extra storage and about an afternoon. Plyr’s whole migration is widening one constant.

The ask

If you write standalone image records — Grain especially — say where this doesn’t fit your schema. The goal: the next reader wires up once and gets everyone.

4 Likes

Instead of defining community.lexicon.media.*, I’d highly recommend making use of ing.dasl.masl, which is about resources in general:

3 Likes

Hey @natespilman.at thanks for the write up. I do wish you had a handwritten introduction even if you got agent help in making the lexicon.

I think this is interesting!

The only other thing I can think of is a pattern that isn’t widely used yet - a raw/original/source field that points to an off protocol larger file.

Or the reverse - as blobs get bigger, does it make sense to generate thumbnails and store them as blobs?

Not my lexicon so up to you all to figure it out!

You’re going to want to find at least one other person to co-lead with you.

2 Likes

Thanks Boris!!

and sorry for the full AI output. I basically wrote 100% of my thoughts into the robot and refined it until I liked it, but I should have just done that on the page itself.

thank you for your reply, and I’ll get back to y’all in a hand written manner soon!

2 Likes

How does one go about putting a lexicon in with ing.dasl.masl? Is there a submission process to get one included under their top level?

Or, is it that you’d make a new lexicon somewhere else (e.g. com.al9000.image) and reference ing.dasl.masl somehow?

Or, would this be something that could be submitted to community.lexicon.media but have the format from ing.dasl.masl?

(I’m new to this and haven’t figured out how that part works)

1 Like

Lexicon schemas are capable of referencing schemas from other namespaces. MASL would sit in between an application-specific schema and the raw blob:

{
  "$type": "example.calendar.event",
  "title": "My awesome birthday party",
  "preview": {
    "$type": "ing.dasl.masl",
    "src": {
      "$link": "bafkrei…"
    },
    "description": "Me posing in front of a gazebo in the park",
    "content-type": "image/jxl",
    "content-length": 68608
  }
}

Or, if the metadata properties are sufficient on their own, the MASL object could be the record itself (within the ing.dasl.masl collection).

3 Likes

I really like MASL (and tiles), but I do want to point out that the lexicon schema for ing.dasl.masl as it is currently deployed ing.dasl.masl Documentation (EN) - Lexicon Garden doesn’t look like that.

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/

3 Likes

Lexicon Community lives here, the blog is our announce / info / source of truth info.

And yes, this lexicon from you is big and opinionated.

We’re encouraging people to scope things down as @natespilman.at has – what is the minimum viable useful for everyone starting point – this gives applications a way to add records in different ways while having the broadest amount of compatibility to aggregate “all images”.

This would be the work of a working group to come to consensus, but here are some blink reactions from me:

  • copyright is immediately going to be contentious and of course applies to things that aren’t images
  • two sets of tags in one record smells bad - why wouldn’t this be an individual image and then point to this as various sorts of collections (e.g. a Grain album)?

(no need to respond or argue this, this is just offered as an example rabbithole which is not strongly held by me)

And, rather than jumping into “here’s what the fields would be”, I would recruit 2+ implementors or intent-to-implementors so this is informed by people that will actually be deploying this.

Nate gave us a thumbnail sketch of something to get started, which is great – now socialize what apps would implement this, THEN form the working group.

6 Likes

Oh I have just started thinking about something similar

I’ve been thinking about a set of small data pieces partially around personal note taking on cultural activity (read this book, went to this gallery, etc) but also a lightweight creator note (I made this etc) just enough to have its existence as a hook available , so others could link it with ( I read this book - link to book culturebloc)

Similarly for venues and shows , I’ve already switched to the lexicon community rsvp etc

interested in being a part of this discussion.

Mark

1 Like

Interested in this. I’m going to do what I did for the app community lexicon: look at how existing lexicons use/store images and identify commonalities + divergences. That should narrow what this looks like, though I think the initial proposal and the one suggested that’s MASL-ish are good starts.

1 Like

Thanks you all so much for all the suggestions and engagement!!

@byarielm.fyi, an audit of existing overlap would be super helpful.

@mfzx.net , I think your suggestion is great, but also tangential to what I’m looking for? If I understand correctly, MASL is the actual rich metadata that would describe an image. I’m interested in something more like standard.site, which is simply a distribution shell - so an arbitrary number of different apps can publish images to the lexicon, which can then be read by an equally arbitrary number of apps. think a MASL body could live inside this distribution, but it’s not top priority. Lmk if I’m understanding this wrong, as it’s entirely new to me!

@bmann.ca I don’t understand how Atmosphere blob storage works to be able to speak intelligently to your questions. That said, a concrete example is - here’s a Luminframe record: PDSls , which references the image in blog storage. I’m thinking that a future state publish workflow on Luminframe would create both its bespoke lexicon record and this new proposed record. the Luminframe could either continue referencing the blob image directly, or instead create the global image version first and then the luminframe.image would reference the community.image record. I prefer the reference idea.

@al9000.com I agree with Boris that it makes sense to keep it small and grow organically, especially because I’m interested in distribution and not in describing an image in full detail. That said, I can forsee this evolving closer and closer to your more robust version as adoption picks up and more use cases are needed.

Thanks y’all. Let me know if I missed anything!!

oh and @al9000.com I agree that pixel width and height make more sense than aspect ratio. aspect ratio can be derived from width and height, but not the other way around.

and @Marksimpkins - I really like the CultureBlocs idea. Would love the collab.

1 Like

I’m totally on board with a smaller, more focused target lexicon. One of my aspirational project is a website builder/CMS that rides on AT Proto. My thinking was from that perspective. Moving forward on something with better general usage makes complete sense.

2 Likes

Yeah in my head I see this as just giving every blob a record (in this case, an image blob). I suspect a general blob record would need too much to make it useable? But worth considering if that’s not the case (thinking scope here; if record for a blob isn’t generalizable enough then the scope gets too big and just image blobs makes sense).

2 Likes

I think there could be something interesting here for “file” metadata if someone wanted to make general file storage out of blobs. But files aren’t images - so yes keep the scope smaller.

2 Likes

I like the idea, as @byarielm.fyi suggested, we can implement this generic file ownership pattern, beginning with file.type === image. Though I think this would require even more community buy in for how low level it is.

1 Like

Yup after looking at lexicons I would agree. It’s likely more valuable to have an image-specific lexicon, video-specific, etc - more scoped to file types type lexicons.

Here’s the table of lexicons - lexicons with images - fyi - pckt
Not exhaustive because there’s so many lexicons now, but think this is pretty representative of how images are used and represented in records + I included some to demonstrate how this can be used widely as a catch-all.

3 Likes

cool, yeah happy to collab :slight_smile: