Christian Heilmann

Author Archive

A behind the scenes of how I create an edition of the WeAreDevelopers Dev Digest – 3 hours in 1 minute speedrun

Wednesday, December 4th, 2024

I write a newsletter every week at https://wearedevelopers.com/newsletter with 150k subscribers. Today I recorded the 3 hours I spent putting the current edition together. Here it is sped up to one minute. I use my browser, lots of copy + paste and VS Code.

What’s your excuse for not using the web share API?

Saturday, November 16th, 2024

The WebShare API is so easy to use, it is a crime people don’t use it more. Instead, we have tons of dead “share on $thing” buttons on the web. Many of which spy on your users and lots of them that started as WordPress plugins but now are security concerns. Instead of guessing how your visitors want to share the current URL or a file you provide, you can call the API and they can pick their favourite:

Animation of the web share API in action. With 10 lines of Code you can turn a button into a share button

This is the code and you can also check it on codepen :

let shareButton = document.querySelector('button');
shareButton.addEventListener("click", async () => {
  try {
    await navigator.share({ title: "Example Page", url: "" });
    console.log("Data was shared successfully");
  } catch (err) {
    console.error("Share failed:", err.message);
  }
});

(yes, I do not use it here, because I want these share buttons to work without JS, but I will soon add this).

Social media platforms should make it easier to add alternative text using an [Alt: description] syntax

Wednesday, November 13th, 2024

Adding alternative texts to images on social platforms is not a “nice thing to have” but important to not lock people out. That’s why it is a shame that it is quite tricky to do it across different platforms. Personally I use Twitter, BlueSky, Mastodon and LinkedIn and the following video shows just how much of an overhead this is.

Adding alternative text to images on various platforms

Sure, I could use mass posting tools like Buffer, but I don’t want to. One thing I have seen people do when others do not add alternative text is answer in the thread with something like:

[Alt: An annoyed user with thick glasses screaming at a laptop]

Why is this not a part of social media platforms? Sure, the editors showing the image bigger and offering a text box below give more context, but they mean yet another interaction which is why people don’t add alternative texts. People tend to drag images in or paste them after they’ve written the post. It feels like a lot of extra work having to go to a different editor, when you’re already in a text box.

What about several images?

This gets a bit trickier, but not insurmountable. Just allow a few of them in succession with a linebreak:

[Alt: An annoyed user with thick glasses screaming at a laptop]
[Alt: A beautiful sunset on a beach with frolicking seals]
[Alt: Elmo of Sesame Street saying that he hopes that you choose to be kind]

Maybe one of the newer platforms could lead the way here. It feels bad to see a huge amount of social posts not having any alternative texts at all – which makes them a lot less social.

Exif by magic – the forgotten extra information in JPEG and TIFF files

Friday, November 1st, 2024

I just shot a ton of pictures on vacation and was amazed to see just how much extra data our mobile phones store in images.

This exif data in JPG and TIFF files can be a privacy issue, which I pointed out in my TEDx talk some time ago and even created a tool to remove the extra information in-browser without the need for any server.

However, it can also be a great thing to use in your products.

You can access EXIF information in many different ways.

What data can you get from an image?

Take the following photo:

A mountain of clay in the sea in corfu

If you use any of the solutions above, you can get the following information from the image. For example, in PHP you can do a:

$file = './Corfu-2024-126.jpg';
$exif = exif_read_data($file);
print_r($exif);

This results in:

Array
(
    [FileName] => Corfu-2024-126.jpg
    [FileDateTime] => 1730189825
    [FileSize] => 3497266
    [FileType] => 2
    [MimeType] => image/jpeg
    [SectionsFound] => ANY_TAG, IFD0, THUMBNAIL, EXIF, GPS, INTEROP
    [COMPUTED] => Array
        (
            [html] => width="4032" height="2268"
            [Height] => 2268
            [Width] => 4032
            [IsColor] => 1
            [ByteOrderMotorola] => 0
            [ApertureFNumber] => f/1.9
            [FocusDistance] => 11.92m
            [Thumbnail.FileType] => 2
            [Thumbnail.MimeType] => image/jpeg
        )
 
    [Make] => Google
    [Model] => Pixel 7 Pro
    [Orientation] => 1
    [XResolution] => 72/1
    [YResolution] => 72/1
    [ResolutionUnit] => 2
    [Software] => HDR+ 1.0.641377693zd
    [DateTime] => 2024:10:09 16:37:40
    [YCbCrPositioning] => 1
    [Exif_IFD_Pointer] => 222
    [GPS_IFD_Pointer] => 960
    [THUMBNAIL] => Array
        (
            [Compression] => 6
            [Orientation] => 1
            [XResolution] => 72/1
            [YResolution] => 72/1
            [ResolutionUnit] => 2
            [JPEGInterchangeFormat] => 1303
            [JPEGInterchangeFormatLength] => 24473
        )
 
    [ExposureTime] => 989/1000000
    [FNumber] => 185/100
    [ExposureProgram] => 2
    [ISOSpeedRatings] => 45
    [ExifVersion] => 0232
    [DateTimeOriginal] => 2024:10:09 16:37:40
    [DateTimeDigitized] => 2024:10:09 16:37:40
    [UndefinedTag:0x9010] => +03:00
    [UndefinedTag:0x9011] => +03:00
    [UndefinedTag:0x9012] => +03:00
    [ComponentsConfiguration] => 
    [ShutterSpeedValue] => 998/100
    [ApertureValue] => 178/100
    [BrightnessValue] => 791/100
    [ExposureBiasValue] => 0/6
    [MaxApertureValue] => 178/100
    [SubjectDistance] => 11921/1000
    [MeteringMode] => 2
    [Flash] => 16
    [FocalLength] => 6810/1000
    [SubSecTime] => 251
    [SubSecTimeOriginal] => 251
    [SubSecTimeDigitized] => 251
    [FlashPixVersion] => 0100
    [ColorSpace] => 1
    [ExifImageWidth] => 4032
    [ExifImageLength] => 2268
    [InteroperabilityOffset] => 930
    [SensingMethod] => 2
    [SceneType] => 
    [CustomRendered] => 1
    [ExposureMode] => 0
    [WhiteBalance] => 0
    [DigitalZoomRatio] => 0/1
    [FocalLengthIn35mmFilm] => 24
    [SceneCaptureType] => 0
    [Contrast] => 0
    [Saturation] => 0
    [Sharpness] => 0
    [SubjectDistanceRange] => 3
    [UndefinedTag:0xA433] => Google
    [UndefinedTag:0xA434] => Pixel 7 Pro back camera 6.81mm f/1.85
    [UndefinedTag:0xA460] => 3
    [GPSVersion] => 
    [GPSLatitudeRef] => N
    [GPSLatitude] => Array
        (
            [0] => 39/1
            [1] => 47/1
            [2] => 4862/100
        )
 
    [GPSLongitudeRef] => E
    [GPSLongitude] => Array
        (
            [0] => 19/1
            [1] => 41/1
            [2] => 5362/100
        )
 
    [GPSAltitudeRef] => 
    [GPSAltitude] => 4200/100
    [GPSTimeStamp] => Array
        (
            [0] => 13/1
            [1] => 37/1
            [2] => 38/1
        )
 
    [GPSImgDirectionRef] => M
    [GPSImgDirection] => 76/1
    [GPSDateStamp] => 2024:10:09
    [InterOperabilityIndex] => R98
    [InterOperabilityVersion] => 0100
)

This is a ton of information to play with. The classic is using the GPS data to show images on a map . If you click on the different thumbnails, you can see the map moving to where the photo was taken.

Map mashup moving a map around to where the photo was taken.

To get this information, all you need to do is read the data and then convert it.

    $exif = exif_read_data($file);
    $lat = $exif['GPSLatitude'];
    $lon = $exif['GPSLongitude'];
    $latref = $exif['GPSLatitudeRef'];
    $lonref = $exif['GPSLongitudeRef'];

One thing I had not done before though was reading the altitude information. This allows you, for example, to show the images on the height they were taken.

Showing the elevation of different images

As the data is returned in a format like “4200/100” you need to do some parsing. This one creates an array of all images with the image name and its height in meters.

$max = 0;
$all = array();
foreach ($jpgs as $jpg) {
    $gps = exif_read_data($jpg);
    if (isset($gps['GPSAltitude'])) {
        $parts = explode('/', $gps['GPSAltitude']);
        $height = round($parts[0] / $parts[1]);
        $jpgs[$jpg]["height"] = $height;
        if ($height > $max) {
            $max = $height;
        }
    }
    array_push($all, array($jpg, $height));
}

Using the embedded thumbnail information

One interesting use case is to use the embedded thumbnail information to avoid having to create thumbnails. If you check the gallery or the height example and you keep developer tools open, you can see that whilst the images are all a few megabytes, the page only reads a few hundred kilobytes.

The gallery with browser developer tools open, showing that only 500 kilobytes were loaded whilst there are several megabytes of images.

This is because we don’t load the images, or created thumbnails from them, but I use the exif thumbnail information in the JPG file instead:

[COMPUTED] => Array
    (
        [html] => width="4032" height="2268"
        [Height] => 2268
        [Width] => 4032
        [IsColor] => 1
        [ByteOrderMotorola] => 0
        [ApertureFNumber] => f/1.9
        [FocusDistance] => 11.92m
        [Thumbnail.FileType] => 2
        [Thumbnail.MimeType] => image/jpeg
    )
…
[THUMBNAIL] => Array
    (
        [Compression] => 6
        [Orientation] => 1
        [XResolution] => 72/1
        [YResolution] => 72/1
        [ResolutionUnit] => 2
        [JPEGInterchangeFormat] => 1303
        [JPEGInterchangeFormatLength] => 24473
    )

Originally, this thumbnail was meant to be displayed on screens built into digital cameras to have a faster preview. We can, however, also use this to preview the image without having to load it in full. In PHP this is the exif_thumbnail() method.

$image = exif_thumbnail('Corfu-2024-991.jpg', $width, $height, $type);
if ($image !== false) {
    header('Content-type: ' .image_type_to_mime_type($type));
    echo $image;
    exit;
} else {
    echo 'No thumbnail available';
}

This will not load the whole JPG file, but only as much as it needs to get the thumbnail. This is not news. Flickr, for example, used this in the early 2000s to show you a thumbnail of the image immediately when you dragged it into the browser and then started to upload the rest. However, I have not seen it used lately. Maybe because it is too easy to create images on the cloud.

Playing with Exif is fun. And why not – after all, our phones and cameras add this information anyway.

I just pulled a 2006 and uploaded my holiday photos to Flickr with a Creative Commons Licence

Friday, October 25th, 2024

I just returned from a holiday on the gorgeous island of Corfu in Greece and spent quite some time taking photos. Instead of releasing those piecemeal on various social media channels, I thought it would be fun to go back to our ways of early social media, and put them all up on Flickr with a CC licence so you can use them. Some make great materials for image manipulation, others are great backdrops for your video calls.

You can see them all on Flickr :

Overview of the six albums on Flickr.

Here are the albums:

For those not on Flickr, you can also get them as Zips:

I really miss the early days of social media and especially the Flickr community. I was lucky enough to have worked with the people who built Flickr and then joined Yahoo and the community aspect of the product was what really made it work. You uploaded a few high quality photos and people would tag themselves and others in them. People would add descriptions and comment in full sentences and not just likes. And the thing that made Flickr really special was that everything was API driven and it was incredibly easy to embed the images in other products and create mashups.

The idea was to add to a web of data and allow your creative output to be re-used, mixed and added to other materials. Not to feed one addiction machine and not even have access to your own work a few seconds later. It is a shame that we lost that, and the bigger issue is that any system that allows for tagging or adding comments drowns in spam and AI slop within seconds now.

We skipped the “social” part of Social Media quite some time ago. But hey, maybe you have some joy seeing how gorgeous this island is!