Automatically serving webp using HashandSalt/kirby3-webp

Hi, I’m using https://github.com/HashandSalt/kirby3-webp to generate webp-versions of uploaded images. Now I’d like to automate things further: Is there a way to automatically serve a webp-version of an image instead of the jpg (if it exists)?

From looking at the code, there is a file method called hasWebp() which checks if for a given file a file with the extension webp is available. So I guess you can use that to serve the webp variant.

But @jimbobrjames as the developer of this plugin will be able to shed more light on this.

@till_k Actually, only serving webp is bad idea. Safari does not support it, apart from the most recent version released in the last software update for Catalina. You will need the fallback for a while yet.

https://caniuse.com/#search=webp

1 Like

And the MS Internet Explorer has no support yet, which is still the standard browser in many companies!

Thank you for the replies, but I think my question wasn’t completely clear: I don’t want to only serve webp, or replace all jpgs with webp-versions. Instead I am looking for a way to find the webp-version of a jpg, in case it exists, so I can serve both versions simultaneously.

And indeed after thinking a bit last night I came up with a solution: a custom file method that takes the file name, strips the extension and then searches for a file with the same name but ending in “.webp” in the same page:

<?php

Kirby::plugin('kammertoens/kirby3-serve-webp', [
    'fileMethods' => [
        'webp' => function () {
            $filename = $this->filename();
			$strippedFilename = substr($filename, 0 , (strrpos($filename, ".")));
			$webpFilename = $strippedFilename . ".webp";
			if ($file = $this->files()->find($webpFilename)):
				return $file;
			else:
				return false;
			endif;
        }
    ]
]);

Since this is the first time I created a plugin or a custom method: Can anybody see any obvious problems with that?

Just now I expanded a bit on that with two new methods: one returns the tag including fallbacks, the other one returns the picture tag with srcsets including fallbacks.

Please let me know if I violated any naming conventions or best practices or did any other stupid mistake:

<?php

Kirby::plugin('kammertoens/kirby3-serve-webp', [
    'fileMethods' => [
        'webp' => function () {
            $filename = $this->filename();
			$strippedFilename = substr($filename, 0 , (strrpos($filename, ".")));
			$webpFilename = $strippedFilename . ".webp";
			if ($file = $this->files()->find($webpFilename)):
				return $file;
			else:
				return false;
			endif;
        },
        'webpPicture' => function () {
        	return '
        		<picture>
					<source srcset="' . $this->webp()->url() . '" type="image/webp">
					<source srcset="' . $this->url() . '" type="image/jpeg">
					<img src="' . $this->url() . '" alt="' . $this->alt() . '">
				</picture>';
        },
        'webpPictureSrcset' => function ($srcset = null) {
        	return '
        		<picture>
					<source srcset="' . $this->webp()->srcset($srcset) . '" type="image/webp">
					<source srcset="' . $this->srcset($srcset) . '" type="image/jpeg">
					<img src="' . $this->url() . '" srcset="' . $this->srcset($srcset) . '" alt="' . $this->alt() . '">
				</picture>';
        }
    ]
]);

I wouldn’t do it like this and use a snippet instead of concatenating your HTML like this.

1 Like

There is a snippet in the plugin thats ready to go. You can overide that instead if you want to adjust it.

It handles the variants (jpegs and webps with the same name) and srcset.

Here is how to use it…

1 Like

Oh I see! I read the readme, but was confused by how the “src” parameter was specified:

snippet('webp', ['sizes' => [1920, 1140, 640, 320], 'src' => 'yourimage.webp', 'type' => 'png', 'class' => 'picturetagclass', 'width' => 800, 'height' => 600])

since it says ‘yourimage.webp’ here, I thought you need to already know the webp-$file for the snippet to work. It wasn’t clear to me that I can just use the jpg $file here as $src and get whole picture tag including the webp-version.

Thanks a lot!

Gotcha, thank you.
As an alternative to a snippet, I took a hint from the $file->html() method and came up with the following. Would that be a more desirable solution?:

'webpPictureSrcset' => function ($srcset = null) {
	$sourceWebp = Html::tag(
		'source', '',
		[
			'srcset' => $this->webp()->srcset($srcset),
			'type' => 'image/webp'
		]
	);
	$sourceJpeg = Html::tag(
		'source', '',
		[
			'srcset' => $this->srcset($srcset),
			'type' => 'image/jpeg'
		]
	);
	$img = Html::img(
		$this->url(),
		[
			'alt' => $this->alt(),
			'srcset' => $this->srcset($srcset)
		]
	);
	return Html::tag('picture', [$sourceWebp, $sourceJpeg, $img]);
},

You an use it in a loop, just replace yourimage.webp with your loop variable. Pick only webp files in the files field… the snippet will find the matching jpegs.

<?php foreach($page->gallery()->toFiles() as $image): ?>
  <?= snippet('webp', ['sizes' => [1920, 1140, 640, 320], 'src' => $image, 'type' => 'jpg', 'class' => 'picturetagclass', 'width' => 800, 'height' => 600]) ?>
<?php endforeach ?>

The above will generate webp files with jpeg fallback from a files field.

I see. I can’t use the snippet then, since in my case the website editors will upload jpgs to a field (a gallery or a slider for example). So i’ll have to use the jpg versions in the loop variable and generate the tag from that. but using the method I posted above that works just fine.
Many thanks for your help!

I am not sure i follow. That’s exactly what the example above does.

gallery()

Is the blue print field accepting uploads etc and the ability to pick the images you wish to be in the slideshow / gallery.

Pick only webp files in the files field […]

When I upload a file to a field of type “files”, your plugin will generate a webp version of that file. The originally uploaded file is available in the field, but the webp is only available in the pages’ files – it won’t get added to the field. Since I have to iterate over the field items, I have to iterate over the jpg files. And didn’t you say the snippet needs the webp file as a parameter?

You can use a filter on the field so that it only shows webp files, and user doesnt even know there are jpegs up there. If you let the user only pick webp files in the files field, the snippet will find the corressponding jpeg.

It will if you set the config option with the same template as the jpeg uploads (the template name you set for uploads on the files field).

'hashandsalt.kirby-webp.template' => 'images', // file blueprint for converted files

I see, I overlooked that option. Unfortunately the site I’m working on is rather large and has different fields on different templates (galleries, sliders, single file fields for one-off images like logos etc) and it would be impractical (or rather impossible) to give all of them the same name.

Anyways, I have a working solution now, just looping over the jpgs and finding the webp versions from there. I’ll share that here once it’s properly tested, in case you’re interested.
Thanks for your help!