<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: images</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/images.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2024-10-06T19:57:00+00:00</updated><author><name>Simon Willison</name></author><entry><title>SVG to JPG/PNG</title><link href="https://simonwillison.net/2024/Oct/6/svg-to-jpg-png/#atom-tag" rel="alternate"/><published>2024-10-06T19:57:00+00:00</published><updated>2024-10-06T19:57:00+00:00</updated><id>https://simonwillison.net/2024/Oct/6/svg-to-jpg-png/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://tools.simonwillison.net/svg-render"&gt;SVG to JPG/PNG&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The latest in my &lt;a href="https://tools.simonwillison.net/"&gt;ongoing series&lt;/a&gt; of interactive HTML and JavaScript tools written almost entirely by LLMs. This one lets you paste in (or open-from-file, or drag-onto-page) some SVG and then use that to render a JPEG or PNG image of your desired width.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of the SVG to JPEG/PNG tool. It starts with a Browse... option for selecting a file, next to a Load example image link, above a textarea full of SVG code. Then a radio box to select between JPEG and PNG, plus a background color color picker widget next to a checkbox labelled transparent. Then Output width, a number field set to 300. Then a convert SVG button. Below is the classic SVG tiger image, with a Download image link that says 47.38BK. Under that is a Base 64 image tag header with a copy image tag button and some visible HTML for a data:image/jpeg image element." src="https://static.simonwillison.net/static/2024/svg-jpg-png.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;I built this using Claude 3.5 Sonnet, initially as an Artifact and later in a code editor since some of the features (loading an example image and downloading the result) cannot run in the sandboxed iframe Artifact environment.&lt;/p&gt;
&lt;p&gt;Here's &lt;a href="https://gist.github.com/simonw/b06fd62ad4e9f8762ad15cdf17e1be85"&gt;the full transcript&lt;/a&gt; of the Claude conversation I used to build the tool, plus &lt;a href="https://github.com/simonw/tools/commits/main/svg-render.html"&gt;a few commits&lt;/a&gt; I later made by hand to further customize it.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/simonw/tools/blob/main/svg-render.html"&gt;code itself&lt;/a&gt; is mostly quite simple. The most interesting part is how it renders the SVG to an image, which (simplified) looks like this:&lt;/p&gt;
&lt;div class="highlight highlight-source-js"&gt;&lt;pre&gt;&lt;span class="pl-c"&gt;// First extract the viewbox to get width/height&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;svgElement&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;DOMParser&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;parseFromString&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;
    &lt;span class="pl-s1"&gt;svgInput&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'image/svg+xml'&lt;/span&gt;
&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;documentElement&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;let&lt;/span&gt; &lt;span class="pl-s1"&gt;viewBox&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;svgElement&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;getAttribute&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'viewBox'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;width&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;height&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;viewBox&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;split&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;' '&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;map&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-v"&gt;Number&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Figure out the width/height of the output image&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;newWidth&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;parseInt&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;widthInput&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;value&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;||&lt;/span&gt; &lt;span class="pl-c1"&gt;800&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;aspectRatio&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;width&lt;/span&gt; &lt;span class="pl-c1"&gt;/&lt;/span&gt; &lt;span class="pl-s1"&gt;height&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;newHeight&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-v"&gt;Math&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;round&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;newWidth&lt;/span&gt; &lt;span class="pl-c1"&gt;/&lt;/span&gt; &lt;span class="pl-s1"&gt;aspectRatio&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Create off-screen canvas&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;canvas&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;createElement&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'canvas'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-s1"&gt;canvas&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;width&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;newWidth&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-s1"&gt;canvas&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;height&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;newHeight&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-c"&gt;// Draw SVG on canvas&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;svgBlob&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;Blob&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s1"&gt;svgInput&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;type&lt;/span&gt;: &lt;span class="pl-s"&gt;'image/svg+xml;charset=utf-8'&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;svgUrl&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;URL&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;createObjectURL&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;svgBlob&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;img&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-v"&gt;Image&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;ctx&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;canvas&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;getContext&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'2d'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-s1"&gt;img&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;onload&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;function&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-s1"&gt;ctx&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;drawImage&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;img&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;newWidth&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;newHeight&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-c1"&gt;URL&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;svgUrl&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-c"&gt;// Convert that to a JPEG&lt;/span&gt;
    &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;imageDataUrl&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;canvas&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;toDataURL&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;"image/jpeg"&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;convertedImg&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;createElement&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;'img'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;convertedImg&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;src&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;imageDataUrl&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-s1"&gt;imageContainer&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;appendChild&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;convertedImg&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-s1"&gt;img&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;src&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;svgUrl&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here's the MDN explanation of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL_static"&gt;that revokeObjectURL() method&lt;/a&gt;, which I hadn't seen before.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Call this method when you've finished using an object URL to let the browser know not to keep the reference to the file any longer.&lt;/p&gt;
&lt;/blockquote&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/svg"&gt;svg&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-artifacts"&gt;claude-artifacts&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude-3-5-sonnet"&gt;claude-3-5-sonnet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-to-app"&gt;prompt-to-app&lt;/a&gt;&lt;/p&gt;



</summary><category term="images"/><category term="javascript"/><category term="svg"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="claude"/><category term="claude-artifacts"/><category term="claude-3-5-sonnet"/><category term="prompt-to-app"/></entry><entry><title>Simple, Fast, and Scalable Reverse Image Search Using Perceptual Hashes and DynamoDB</title><link href="https://simonwillison.net/2022/Oct/19/simple-fast-and-scalable-reverse-image-search-using-perceptual-h/#atom-tag" rel="alternate"/><published>2022-10-19T15:04:58+00:00</published><updated>2022-10-19T15:04:58+00:00</updated><id>https://simonwillison.net/2022/Oct/19/simple-fast-and-scalable-reverse-image-search-using-perceptual-h/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://canvatechblog.com/simple-fast-and-scalable-reverse-image-search-using-perceptual-hashes-and-dynamodb-df3007d19934"&gt;Simple, Fast, and Scalable Reverse Image Search Using Perceptual Hashes and DynamoDB&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Christopher Bong provides a clear explanation of how perceptual hashes can be used to create a string representing the visual content of an image, such that similar images can be identified by calculating a hamming distance between those hashes. He then explains how they built a large-scale system for this at Canva on top of DynamoDB, by splitting those strings into smaller hash windows and using those for efficient bulk lookups of similar candidates.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/search"&gt;search&lt;/a&gt;&lt;/p&gt;



</summary><category term="images"/><category term="search"/></entry><entry><title>Making GitHub’s new homepage fast and performant</title><link href="https://simonwillison.net/2021/Jan/29/github-homepage/#atom-tag" rel="alternate"/><published>2021-01-29T19:05:34+00:00</published><updated>2021-01-29T19:05:34+00:00</updated><id>https://simonwillison.net/2021/Jan/29/github-homepage/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.blog/2021-01-29-making-githubs-new-homepage-fast-and-performant/"&gt;Making GitHub’s new homepage fast and performant&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A couple of really clever tricks in this article by Tobias Ahlin. The first is using IntersectionObserver in conjunction with the video preload=“none” attribute to lazily load a video when it scrolls into view. The second is an ingenious trick to create an efficiently encoded transparent JPEG image: embed the image in a SVG file twice, once as the image and once as a transparency mask.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/performance"&gt;performance&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/svg"&gt;svg&lt;/a&gt;&lt;/p&gt;



</summary><category term="github"/><category term="images"/><category term="javascript"/><category term="performance"/><category term="svg"/></entry><entry><title>AVIF has landed</title><link href="https://simonwillison.net/2020/Sep/9/avif-has-landed/#atom-tag" rel="alternate"/><published>2020-09-09T16:49:24+00:00</published><updated>2020-09-09T16:49:24+00:00</updated><id>https://simonwillison.net/2020/Sep/9/avif-has-landed/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://jakearchibald.com/2020/avif-has-landed/"&gt;AVIF has landed&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
AVIF support landed in Chrome 85 a few weeks ago. It’s a new lossy royalty-free image format derived from AV1 video and it’s really impressive—it can achieve similar results to JPEG using a quarter of the file size! Jake digs into AVIF in detail, providing lots of illustrative examples created using the Squoosh online compressor, which now supports AVIF encoding. Jake used the same WebAssembly encoder from Squoosh to decode AVIF images in a web worker so that the demos in his article would work even for browsers that don’t yet support AVIF natively.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webworkers"&gt;webworkers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jake-archibald"&gt;jake-archibald&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="images"/><category term="webworkers"/><category term="webassembly"/><category term="jake-archibald"/></entry><entry><title>datasette-media 0.4</title><link href="https://simonwillison.net/2020/Jul/28/datasette-media/#atom-tag" rel="alternate"/><published>2020-07-28T02:22:17+00:00</published><updated>2020-07-28T02:22:17+00:00</updated><id>https://simonwillison.net/2020/Jul/28/datasette-media/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/simonw/datasette-media/releases/tag/0.4"&gt;datasette-media 0.4&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
datasette-media is my Datasette plugin for serving media (e.g. images) directly from Datasette. The first version used file paths saved in a column and served the data from disk—this new version adds the ability to serve content from BLOB columns, such as those created by the new “sqlite-utils insert-files” command. It also adds configurable support for resizing images based on querystring parameters like ?w=100.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/plugins"&gt;plugins&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;



</summary><category term="images"/><category term="plugins"/><category term="projects"/><category term="datasette"/></entry><entry><title>Squoosh</title><link href="https://simonwillison.net/2018/Nov/12/squoosh/#atom-tag" rel="alternate"/><published>2018-11-12T23:15:34+00:00</published><updated>2018-11-12T23:15:34+00:00</updated><id>https://simonwillison.net/2018/Nov/12/squoosh/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://squoosh.app/"&gt;Squoosh&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is by far the most useful example of web assembly I’ve seen so far: Squoosh is a progressive web app for image optimization (JPEG, PNG, GIF, SVG and more) which uses emscripten-compiled versions of best in breed image codec implementations to provide a browser interface for applying and previewing those optimizations.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-performance"&gt;web-performance&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;&lt;/p&gt;



</summary><category term="images"/><category term="web-performance"/><category term="webassembly"/></entry><entry><title>dhash</title><link href="https://simonwillison.net/2017/Nov/9/dhash/#atom-tag" rel="alternate"/><published>2017-11-09T17:44:52+00:00</published><updated>2017-11-09T17:44:52+00:00</updated><id>https://simonwillison.net/2017/Nov/9/dhash/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Jetsetter/dhash"&gt;dhash&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Python library to calculate the perceptual difference hash for an image. Delightfully simple algorithm that’s fully explained in the README—it works by scaling the image to 8x8 grayscale and then creating a bitmap representing of each pixel is lighter or darker than the previous one.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://benhoyt.com/projects/"&gt;Ben Hoyt&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;&lt;/p&gt;



</summary><category term="images"/><category term="python"/></entry><entry><title>I pushed 20 more of my projects to GitHub</title><link href="https://simonwillison.net/2010/Sep/23/pushed/#atom-tag" rel="alternate"/><published>2010-09-23T01:18:00+00:00</published><updated>2010-09-23T01:18:00+00:00</updated><id>https://simonwillison.net/2010/Sep/23/pushed/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.catonmat.net/blog/i-pushed-20-more-of-my-projects-to-github?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A catonmat %28good coders code%2C great reuse%29"&gt;I pushed 20 more of my projects to GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Some great Node.js stuff here from Peteris Krumins, including modules for processing PNG, JPEG and animated GIFs.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nodejs"&gt;nodejs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/png"&gt;png&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jpeg"&gt;jpeg&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/peteris-krumins"&gt;peteris-krumins&lt;/a&gt;&lt;/p&gt;



</summary><category term="images"/><category term="nodejs"/><category term="png"/><category term="recovered"/><category term="jpeg"/><category term="peteris-krumins"/></entry><entry><title>Placehold.it</title><link href="https://simonwillison.net/2010/Mar/20/placeholdit/#atom-tag" rel="alternate"/><published>2010-03-20T14:32:23+00:00</published><updated>2010-03-20T14:32:23+00:00</updated><id>https://simonwillison.net/2010/Mar/20/placeholdit/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://placehold.it/"&gt;Placehold.it&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Useful dynamic image generator for layout mockups—just drop an image in to a page pointing at http://placehold.it/300x200. Takes optional arguments for text, colour and format as well.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/design"&gt;design&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/layout"&gt;layout&lt;/a&gt;&lt;/p&gt;



</summary><category term="design"/><category term="images"/><category term="layout"/></entry><entry><title>moddims</title><link href="https://simonwillison.net/2009/Jul/21/moddims/#atom-tag" rel="alternate"/><published>2009-07-21T18:18:15+00:00</published><updated>2009-07-21T18:18:15+00:00</updated><id>https://simonwillison.net/2009/Jul/21/moddims/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://code.google.com/p/moddims/"&gt;moddims&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Apache 2 module which exposes ImageMagick as a URL-driven service, allowing you to request an image from a whitelisted host server and resize, thumbnail or alter the quality of it.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://www.artzstudio.com/2009/06/web-performance-impact-on-revenue-velocity-09-highlights/"&gt;Dave Artz&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/apache"&gt;apache&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/imagemagick"&gt;imagemagick&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/moddims"&gt;moddims&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/resizing"&gt;resizing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/thumbnails"&gt;thumbnails&lt;/a&gt;&lt;/p&gt;



</summary><category term="apache"/><category term="imagemagick"/><category term="images"/><category term="moddims"/><category term="resizing"/><category term="thumbnails"/></entry><entry><title>The GIF Pronunciation Page</title><link href="https://simonwillison.net/2009/Jun/11/gif/#atom-tag" rel="alternate"/><published>2009-06-11T22:50:33+00:00</published><updated>2009-06-11T22:50:33+00:00</updated><id>https://simonwillison.net/2009/Jun/11/gif/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.olsenhome.com/gif/"&gt;The GIF Pronunciation Page&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
It’s jiff. Here’s evidence.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/gif"&gt;gif&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pronunciation"&gt;pronunciation&lt;/a&gt;&lt;/p&gt;



</summary><category term="gif"/><category term="images"/><category term="pronunciation"/></entry><entry><title>Load Windows ICO files</title><link href="https://simonwillison.net/2009/Jan/17/ico/#atom-tag" rel="alternate"/><published>2009-01-17T21:48:49+00:00</published><updated>2009-01-17T21:48:49+00:00</updated><id>https://simonwillison.net/2009/Jan/17/ico/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/1287/"&gt;Load Windows ICO files&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Apparently PIL has trouble with the most recent versions of the windows .ico format (Vista now embeds PNG images in them)—this clever function deals with the differences and gives back a PIL Image object.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/ico"&gt;ico&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pil"&gt;pil&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/png"&gt;png&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vista"&gt;vista&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/windows"&gt;windows&lt;/a&gt;&lt;/p&gt;



</summary><category term="ico"/><category term="images"/><category term="pil"/><category term="png"/><category term="python"/><category term="vista"/><category term="windows"/></entry><entry><title>VectorMagic</title><link href="https://simonwillison.net/2007/Oct/28/vectormagic/#atom-tag" rel="alternate"/><published>2007-10-28T11:46:44+00:00</published><updated>2007-10-28T11:46:44+00:00</updated><id>https://simonwillison.net/2007/Oct/28/vectormagic/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://vectormagic.stanford.edu/"&gt;VectorMagic&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Neat online tool (with a Flex frontend) for tracing bitmap images in to vectors, based on research at the Stanford AI lab.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/flash"&gt;flash&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/flex"&gt;flex&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/graphics"&gt;graphics&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/stanford"&gt;stanford&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vectormagic"&gt;vectormagic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vectors"&gt;vectors&lt;/a&gt;&lt;/p&gt;



</summary><category term="flash"/><category term="flex"/><category term="graphics"/><category term="images"/><category term="stanford"/><category term="vectormagic"/><category term="vectors"/></entry><entry><title>Return of the HTTP overhead delay</title><link href="https://simonwillison.net/2007/Jul/11/return/#atom-tag" rel="alternate"/><published>2007-07-11T15:12:45+00:00</published><updated>2007-07-11T15:12:45+00:00</updated><id>https://simonwillison.net/2007/Jul/11/return/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.wait-till-i.com/index.php?p=465"&gt;Return of the HTTP overhead delay&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Christian proposes a neat way of improving page performance, by delaying non-essential images such as avatars until after the rest of the page has loaded.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/avatars"&gt;avatars&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/christian-heilmann"&gt;christian-heilmann&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http"&gt;http&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/onload"&gt;onload&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/performance"&gt;performance&lt;/a&gt;&lt;/p&gt;



</summary><category term="avatars"/><category term="christian-heilmann"/><category term="http"/><category term="images"/><category term="javascript"/><category term="onload"/><category term="performance"/></entry></feed>