A screenshot showing a tweet with an image and highlighting the code with alternative text.

Creative bots and accessibility

Accessibility on the web is an important topic that’s thankfully being taken more seriously nowadays. And there’s a few things you can do, as a creative botmaker, to ensure that your bots can be enjoyed by everyone.

Add image descriptions

When you upload an image on sites like Mastodon, Twitter, or LinkedIn, you will have an option to add a description to it that will be used by screen readers and thus making it possible for people who can’t see the image to get an idea what’s in it.

And when you make a bot that posts or generates images, you can use the API to do the same. Let me show you how.

On Twitter, we can use the media/metadata/create endpoint. Check out the documentation for it here.

In short, after you upload an image, you can reference its ID and pass it to this endpoint together with an alt_text object that contains the description.

You can see the full code on GitHub, here’s what the relevant part looks like:

T.post('media/upload', {media_data: imageData}, (err, data, response) => {
    /* First we upload the image. */
    if (err){
        console.log('error:', err);
    } else {
        const image = data;
        /* Now we can add image description. */
        T.post('media/metadata/create', {
            media_id: data.media_id_string,
            alt_text: {
                text: 'Describe the image'
            }            
        }, (err, data, response) => {
            /* And finally, post a tweet with the image. */
            T.post('statuses/update', {
                // status: 'Optional tweet text.',
                media_ids: new Array(image.media_id_string)
            }, (err, data, response) => {
                if (err){
                    console.log('error:', err);
                }
           );
        });
    }
});

And on Mastodon, you can pass the description parameter do to the same.

client.post('media', {
  file: fs.createReadStream(filePath),
  description: 'Describe the image'
}, (err, data, response) => {
  if (err){
    console.log('mastodon.postImage error:', err);
  }
  else{
    const statusObj = {
      status: 'Optional text.',
      media_ids: new Array(data.id)
    }

    client.post('statuses', statusObj,
    (err, data, response) => {
      if (err){
        console.log('mastodon.postImage error:', err);
      } 
    });
  }
});

It’s a pretty easy and straightforward way to make sure everyone gets to enjoy your bot.

For some pointers on how to write a good image description, you can read this article from Harvard University.

How do you typically handle image description for your image bots?

Feel free to share any useful tips!

— botwiki.org (@botwiki@mastodon.social) 2023-04-25T15:14:13.690Z

You might not always know what’s in the image ahead of time, which is the case with one of my bots, @nycviewsbot.

What I decided to do is to describe the view that’s usually in the view.

const webcams = [
  {
    id: 1639820559,
    title: "New York › South-West",
    description:
      "This webcam usually shows a view of the lower Manhattan with its tall buildings and skyscrapers.",
    url: "https://images-webcams.windy.com/59/1639820559/current/preview/1639820559.jpg",
    latitude: 40.712728,
    longitude: -74.006015,
  },
  {
    id: 1577050386,
    title: "Manhattan Community Board 1 › North",
    description:
      "This webcam usually shows a view of the Brooklyn Bridge, with Brooklyn closer to the webcam, and Manhattan further away.",
    url: "https://images-webcams.windy.com/86/1577050386/current/preview/1577050386.jpg",
    latitude: 40.705222,
    longitude: -73.995946,
  },
  {
    id: 1649876051,
    title: "Weehawken: 1560 Broadway - Times Square",
    description:
      "This webcam usually shows a close-up view of the buildings and billboard ads at the Times Square.",
    url: "https://images-webcams.windy.com/51/1649876051/current/preview/1649876051.jpg",
    latitude: 40.758616,
    longitude: -73.984874,
  },
];

I am also using a weather API from my web API tutorial for additional context.

const owmApiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${webcam.latitude}&lon=${webcam.longitude}&units=imperial&APPID=${process.env.OWM_APP_ID}`;
let weather = null;

request(owmApiUrl, (error, response, body) => {
  if (!error) {
    try {
      const responseJSON = JSON.parse(body);
      const temperature = `It's ${Math.round(responseJSON.main.temp)} °F.`;

      switch (responseJSON.weather[0].main) {
        case "Clear":
          weather = `It's a clear day in New York. ${temperature}`;
          break;
// ...

Together, it might look like this.

An example post from the NYC Views bot showing a webcam image with a description:

Another option to consider might be using an image recognition API. You will have to get creative here and see what works best for your particular image set.

Properly capitalize hashtags

If your bot uses hashtags, another good practice is to capitalize the first letter of each word. So instead #generativeart you should use #GenerativeArt. This is useful for screen readers, but also helps folks with dyslexia or cognitive disabilities.

Don’t use special characters that look like letters

When you use characters that look like letters, for example 𝖃 instead of the letter X, screen readers read the full name of such symbols, in our example that would be “Mathematical Bold Fraktur Capital X”.

Watch the video below to get a full sense of what the experience is like.

If you have any more tips, feel free to share them via email or Twitter DM!

More tutorials

A tinted, zoomed in screenshot of a JSON object showing server information about a Mastodon instance.
A tinted screenshot of two charts, one showing the popularity of various fediverse platforms (with Mastodon far ahead of the rest), and the other chart showing distribution of domain creation dates, mostly clustered around 2023.
A tinted screenshot showing the @mtaupdates Mastodon profile and a few example posts with subway status alerts.

💻 Browse all