123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- <!doctype html><!-- This is a valid HTML5 document. -->
- <!-- Screen readers, SEO, extensions and so on. -->
- <html lang="fr">
- <!-- Has to be within the first 1024 bytes, hence before the <title>
- See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
- <meta charset="utf-8">
- <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
- <!-- The viewport meta is quite crowded and we are responsible for that.
- See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <!-- Required to make a valid HTML5 document. -->
- <title>Representing SHA-256 Hashes As Avatars (archive) — David Larlet</title>
- <meta name="description" content="Publication mise en cache pour en conserver une trace.">
- <!-- That good ol' feed, subscribe :). -->
- <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
- <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
- <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
- <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
- <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
- <link rel="manifest" href="/static/david/icons2/site.webmanifest">
- <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
- <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
- <meta name="msapplication-TileColor" content="#f0f0ea">
- <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
- <meta name="theme-color" content="#f0f0ea">
- <!-- Documented, feel free to shoot an email. -->
- <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
- <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <script>
- function toggleTheme(themeName) {
- document.documentElement.classList.toggle(
- 'forced-dark',
- themeName === 'dark'
- )
- document.documentElement.classList.toggle(
- 'forced-light',
- themeName === 'light'
- )
- }
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme !== 'undefined') {
- toggleTheme(selectedTheme)
- }
- </script>
-
- <meta name="robots" content="noindex, nofollow">
- <meta content="origin-when-cross-origin" name="referrer">
- <!-- Canonical URL for SEO purposes -->
- <link rel="canonical" href="https://francoisbest.com/posts/2021/hashvatars">
-
- <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick">
-
- <article>
- <header>
- <h1>Representing SHA-256 Hashes As Avatars</h1>
- </header>
- <nav>
- <p class="center">
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="https://francoisbest.com/posts/2021/hashvatars" title="Lien vers le contenu original">Source originale</a>
- </p>
- </nav>
- <hr>
- <p class="chakra-text css-8fkoyy">If you <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://twitter.com/fortysevenfx">follow me on Twitter</a>, you may be aware
- of my weird <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://twitter.com/search?q=%40fortysevenfx%20%22weekend%20project%22">weekend projects</a>.</p>
-
- <p class="chakra-text css-8fkoyy">They are little challenges I give myself, usually without too many stakes
- involved, and with small enough a scope so that I can ship it in a day or two,
- while keeping spare family time.</p>
-
- <p class="chakra-text css-8fkoyy">This weekend's project is to build a transaction explorer for the <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://centralized-coin.com">Centralized Coin</a>
- experiment. Something like a blockchain explorer, but without the crypto overhead.</p>
-
- <p class="chakra-text css-8fkoyy">Each node in the system is represented by a hash. Because humans are
- terrible at reading and quickly identifying large numbers (other than by their
- first or last few digits), a visual representation is needed.</p>
-
- <p class="chakra-text css-8fkoyy">There are <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://barro.github.io/2018/02/avatars-identicons-and-hash-visualization/">many solutions</a>
- out there: WordPress and GitHub identicons, <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://robohash.org/">robohash</a>, monsterID etc..</p>
-
- <p class="chakra-text css-8fkoyy">I wanted one that still looks abstract (not as opinionated as monsters or robots),
- and that plays nice in a rounded avatar UI component.</p>
-
- <p class="chakra-text css-8fkoyy">This is what I ended up with:<br><small class="chakra-text css-1lrujbg">Change the input below to see how the SHA-256 hash changes the render</small></p>
-
- <p class="chakra-text css-8fkoyy">If you are allergic to maths and trigonometry, feel free to play with the more
- detailed <a class="chakra-link css-y3jojq" href="/hashvatar">interactive example here</a>. Otherwise, let's dive into
- how it's done.</p>
-
- <h2 class="chakra-heading css-1rjvsw4" id="space-partitioning">Space Partitioning<a class="chakra-link css-jchjfc" aria-label="anchor" href="#space-partitioning">#</a></h2>
-
- <p class="chakra-text css-8fkoyy">Many of the existing solutions produce square images, yet avatars are often
- displayed as circles. They would lose information on the corners when rounded,
- so instead of a cartesian (x-y) approach, we're going to use polar (angle-radius)
- coordinates instead.</p>
-
- <p class="chakra-text css-8fkoyy">A grid in cartesian space maps to concentric circles and pie-like cuts in
- polar space.</p>
-
- <p class="chakra-text css-8fkoyy">SHA-256 hashes have 256 bits of information that we need to represent. Dividing
- a circle into 256 sections would make each section too small to be visually useful,
- and would only leave 1 bit of "value" to represent in each section
- (0 or 1, black or white).</p>
-
- <p class="chakra-text css-8fkoyy">Instead, we're going to divide the circle into 32 sections:</p>
-
- <ul role="list" class="css-80rt2u"><li class="css-77vupy">4 concentric rings</li><li class="css-77vupy">8 pie-like cuts</li></ul>
-
- <p class="chakra-text css-8fkoyy">The resulting SVG code for such a grid looks like this (in a React component):</p>
-
- <pre class="language-tsx"><code class="language-tsx"><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">SHA256Avatar</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
-
- <span class="token keyword">const</span> r1 <span class="token operator">=</span> <span class="token number">1</span>
- <span class="token keyword">const</span> r2 <span class="token operator">=</span> r1 <span class="token operator">*</span> <span class="token number">0.75</span>
- <span class="token keyword">const</span> r3 <span class="token operator">=</span> r1 <span class="token operator">*</span> <span class="token number">0.5</span>
- <span class="token keyword">const</span> r4 <span class="token operator">=</span> r1 <span class="token operator">*</span> <span class="token number">0.25</span>
- <span class="token keyword">return</span> <span class="token punctuation">(</span>
- <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>-1 -1 2 2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">cx</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">cy</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">r</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r1<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">cx</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">cy</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">r</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r2<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">cx</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">cy</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">r</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r3<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">cx</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">cy</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">r</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r4<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>line</span> <span class="token attr-name">x1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token operator">-</span>r1<span class="token punctuation">}</span></span> <span class="token attr-name">x2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r1<span class="token punctuation">}</span></span> <span class="token attr-name">y1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">y2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>line</span> <span class="token attr-name">y1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token operator">-</span>r1<span class="token punctuation">}</span></span> <span class="token attr-name">y2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r1<span class="token punctuation">}</span></span> <span class="token attr-name">x1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token attr-name">x2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>line</span>
- <span class="token attr-name">x1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token operator">-</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token attr-name">x2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token attr-name">y1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token operator">-</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token attr-name">y2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>line</span>
- <span class="token attr-name">x1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token attr-name">x2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token operator">-</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token attr-name">y1</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token operator">-</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token attr-name">y2</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>r1 <span class="token operator">*</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">SQRT1_2</span><span class="token punctuation">}</span></span>
- <span class="token punctuation">/></span></span><span class="token plain-text">
- </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span>
- <span class="token punctuation">)</span>
- <span class="token punctuation">}</span>
- </code></pre>
-
- <p class="chakra-text css-8fkoyy">Doing this naively, with each concentric ring's radius being 1/4th of the
- outermost/largest one gives us this:</p>
-
- <p><svg xmlns="http://www.w3.org/2000/svg" role="img" viewbox="-1 -1 2 2" class="css-q1xci"><g><path d="M 0 0 L 6.123233995736766e-17 -1 A 1 1 0 0 1 0.7071067811865476 -0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 -0.7071067811865475 A 1 1 0 0 1 1 0 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 1 0 A 1 1 0 0 1 0.7071067811865476 0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 0.7071067811865475 A 1 1 0 0 1 6.123233995736766e-17 1 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 6.123233995736766e-17 1 A 1 1 0 0 1 -0.7071067811865475 0.7071067811865476 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865475 0.7071067811865476 A 1 1 0 0 1 -1 1.2246467991473532e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1 1.2246467991473532e-16 A 1 1 0 0 1 -0.7071067811865477 -0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865477 -0.7071067811865475 A 1 1 0 0 1 -1.8369701987210297e-16 -1 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1.3777276490407724e-16 -0.75 A 0.75 0.75 0 0 1 0.5303300858899105 -0.5303300858899107 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5303300858899105 -0.5303300858899107 A 0.75 0.75 0 0 1 0.75 -1.8369701987210297e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.75 -1.8369701987210297e-16 A 0.75 0.75 0 0 1 0.5303300858899113 0.53033008588991 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5303300858899113 0.53033008588991 A 0.75 0.75 0 0 1 2.2962127484012875e-16 0.75 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 2.2962127484012875e-16 0.75 A 0.75 0.75 0 0 1 -0.5303300858899109 0.5303300858899104 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5303300858899109 0.5303300858899104 A 0.75 0.75 0 0 1 -0.75 2.755455298081545e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.75 2.755455298081545e-16 A 0.75 0.75 0 0 1 -0.5303300858899114 -0.5303300858899099 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5303300858899114 -0.5303300858899099 A 0.75 0.75 0 0 1 -3.214697847761802e-16 -0.75 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -2.143131898507868e-16 -0.5 A 0.5 0.5 0 0 1 0.3535533905932739 -0.3535533905932736 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.3535533905932739 -0.3535533905932736 A 0.5 0.5 0 0 1 0.5 -2.4492935982947064e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5 -2.4492935982947064e-16 A 0.5 0.5 0 0 1 0.3535533905932743 0.3535533905932733 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.3535533905932743 0.3535533905932733 A 0.5 0.5 0 0 1 2.755455298081545e-16 0.5 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 2.755455298081545e-16 0.5 A 0.5 0.5 0 0 1 -0.35355339059327384 0.3535533905932737 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.35355339059327384 0.3535533905932737 A 0.5 0.5 0 0 1 -0.5 1.1943401194869635e-15 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5 1.1943401194869635e-15 A 0.5 0.5 0 0 1 -0.3535533905932737 -0.35355339059327384 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.3535533905932737 -0.35355339059327384 A 0.5 0.5 0 0 1 -1.2249562894656473e-15 -0.5 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -6.124781447328236e-16 -0.25 A 0.25 0.25 0 0 1 0.17677669529663723 -0.17677669529663653 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.17677669529663723 -0.17677669529663653 A 0.25 0.25 0 0 1 0.25 -1.8369701987210297e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.25 -1.8369701987210297e-16 A 0.25 0.25 0 0 1 0.17677669529663748 0.17677669529663628 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.17677669529663748 0.17677669529663628 A 0.25 0.25 0 0 1 -2.450841049886177e-16 0.25 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -2.450841049886177e-16 0.25 A 0.25 0.25 0 0 1 -0.1767766952966366 0.1767766952966372 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.1767766952966366 0.1767766952966372 A 0.25 0.25 0 0 1 -0.25 1.1024916095509121e-15 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.25 1.1024916095509121e-15 A 0.25 0.25 0 0 1 -0.1767766952966369 -0.1767766952966369 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.1767766952966369 -0.1767766952966369 A 0.25 0.25 0 0 1 -6.737104846901913e-16 -0.25 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path></g></svg><p class="chakra-text css-8fkoyy">There are some issues: the innermost ring sections are tiny compared to the outermost.</p><p class="chakra-text css-8fkoyy">If we calculate the radii so that each section has an equal area, we get
- the following result:</p><svg xmlns="http://www.w3.org/2000/svg" role="img" viewbox="-1 -1 2 2" class="css-q1xci"><g><path d="M 0 0 L 6.123233995736766e-17 -1 A 1 1 0 0 1 0.7071067811865476 -0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 -0.7071067811865475 A 1 1 0 0 1 1 0 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 1 0 A 1 1 0 0 1 0.7071067811865476 0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 0.7071067811865475 A 1 1 0 0 1 6.123233995736766e-17 1 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 6.123233995736766e-17 1 A 1 1 0 0 1 -0.7071067811865475 0.7071067811865476 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865475 0.7071067811865476 A 1 1 0 0 1 -1 1.2246467991473532e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1 1.2246467991473532e-16 A 1 1 0 0 1 -0.7071067811865477 -0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865477 -0.7071067811865475 A 1 1 0 0 1 -1.8369701987210297e-16 -1 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1.5908628580873602e-16 -0.8660254037844386 A 0.8660254037844386 0.8660254037844386 0 0 1 0.6123724356957944 -0.6123724356957946 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.6123724356957944 -0.6123724356957946 A 0.8660254037844386 0.8660254037844386 0 0 1 0.8660254037844386 -2.1211504774498136e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.8660254037844386 -2.1211504774498136e-16 A 0.8660254037844386 0.8660254037844386 0 0 1 0.6123724356957952 0.6123724356957938 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.6123724356957952 0.6123724356957938 A 0.8660254037844386 0.8660254037844386 0 0 1 2.6514380968122674e-16 0.8660254037844386 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 2.6514380968122674e-16 0.8660254037844386 A 0.8660254037844386 0.8660254037844386 0 0 1 -0.6123724356957948 0.6123724356957941 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.6123724356957948 0.6123724356957941 A 0.8660254037844386 0.8660254037844386 0 0 1 -0.8660254037844386 3.1817257161747205e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.8660254037844386 3.1817257161747205e-16 A 0.8660254037844386 0.8660254037844386 0 0 1 -0.6123724356957952 -0.6123724356957937 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.6123724356957952 -0.6123724356957937 A 0.8660254037844386 0.8660254037844386 0 0 1 -3.7120133355371736e-16 -0.8660254037844386 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -3.030846196824227e-16 -0.7071067811865476 A 0.7071067811865476 0.7071067811865476 0 0 1 0.5000000000000002 -0.49999999999999983 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5000000000000002 -0.49999999999999983 A 0.7071067811865476 0.7071067811865476 0 0 1 0.7071067811865476 -3.4638242249419736e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 -3.4638242249419736e-16 A 0.7071067811865476 0.7071067811865476 0 0 1 0.5000000000000008 0.4999999999999994 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5000000000000008 0.4999999999999994 A 0.7071067811865476 0.7071067811865476 0 0 1 3.8968022530597204e-16 0.7071067811865476 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 3.8968022530597204e-16 0.7071067811865476 A 0.7071067811865476 0.7071067811865476 0 0 1 -0.5000000000000001 0.4999999999999999 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5000000000000001 0.4999999999999999 A 0.7071067811865476 0.7071067811865476 0 0 1 -0.7071067811865476 1.6890519950647668e-15 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865476 1.6890519950647668e-15 A 0.7071067811865476 0.7071067811865476 0 0 1 -0.4999999999999999 -0.5000000000000001 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.4999999999999999 -0.5000000000000001 A 0.7071067811865476 0.7071067811865476 0 0 1 -1.7323497978765413e-15 -0.7071067811865476 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1.2249562894656473e-15 -0.5 A 0.5 0.5 0 0 1 0.35355339059327445 -0.35355339059327306 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.35355339059327445 -0.35355339059327306 A 0.5 0.5 0 0 1 0.5 -3.6739403974420594e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5 -3.6739403974420594e-16 A 0.5 0.5 0 0 1 0.35355339059327495 0.35355339059327257 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.35355339059327495 0.35355339059327257 A 0.5 0.5 0 0 1 -4.901682099772354e-16 0.5 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -4.901682099772354e-16 0.5 A 0.5 0.5 0 0 1 -0.3535533905932732 0.3535533905932744 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.3535533905932732 0.3535533905932744 A 0.5 0.5 0 0 1 -0.5 2.2049832191018243e-15 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5 2.2049832191018243e-15 A 0.5 0.5 0 0 1 -0.3535533905932738 -0.3535533905932738 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.3535533905932738 -0.3535533905932738 A 0.5 0.5 0 0 1 -1.3474209693803826e-15 -0.5 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path></g></svg><div role="alert" class="chakra-alert css-7l7mra"><div class="chakra-alert__desc css-12db8z9"><p>Equal areas are calculated by solving a system of equations.</p><ol role="list" class="css-138fnjp"><li class="css-77vupy">Each section area is 1/32nd of the area of the whole circle. Assuming the outer circle has a radius of 1, that's an area of <code>π/32</code>.</li><li class="css-77vupy">To compute the associated radius for a ring, we express the pie slice area with the outer radius R and subtract the pie slice area with the inner radius r: <code>Pi R^2 - Pi r^2</code>, then we iterate from the outside in.</li></ol><a target="_blank" rel="noopener noreferrer" class="chakra-link css-zqkdi0" href="https://www.desmos.com/calculator/cenpmgvqdy">More details</a></div></div><p class="chakra-text css-8fkoyy">Not very pleasing either. How about a mix of both ?<br><small class="chakra-text css-0">(Play with the slider to blend between equal radii and equal areas)</small></p><p class="chakra-text css-8fkoyy">I don't know about you, but <code class="chakra-code css-1uwjdr8">0.42</code> hits the ballpark both in aesthetics and
- <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://xkcd.com/356/">nerd-sniping satisfaction</a>, so let's go for that.</p><h2 class="chakra-heading css-1rjvsw4" id="section-mapping">Section Mapping<a class="chakra-link css-jchjfc" aria-label="anchor" href="#section-mapping">#</a></h2><p class="chakra-text css-8fkoyy">Now that we have 32 nice looking sections on our circle, we can map each section
- to an 8-bit value in the hash.</p><p class="chakra-text css-8fkoyy">As an example, let's take the following hash, the output of <code class="chakra-code css-1uwjdr8">sha256("Hello, world!")</code>:</p><pre><code>315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3
- </code></pre><p class="chakra-text css-8fkoyy">We can split it in 32 blocks of 8 bits (2 hexadecimal digits), and organise them by
- 4 blocks of 8 to map to the rings:</p><pre><code>12 o'clock -> clockwise
- 31 5f 5b db 76 d0 78 c4 Outer ring
- 3b 8a c0 06 4e 4a 01 64 Middle-outer ring
- 61 2b 1f ce 77 c8 69 34 Middle-inner ring
- 5b fc 94 c7 58 94 ed d3 Inner ring
- </code></pre><svg xmlns="http://www.w3.org/2000/svg" role="img" viewbox="-1 -1 2 2" class="css-q1xci"><g><path d="M 0 0 L 6.123233995736766e-17 -1 A 1 1 0 0 1 0.7071067811865476 -0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 -0.7071067811865475 A 1 1 0 0 1 1 0 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 1 0 A 1 1 0 0 1 0.7071067811865476 0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 0.7071067811865475 A 1 1 0 0 1 6.123233995736766e-17 1 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 6.123233995736766e-17 1 A 1 1 0 0 1 -0.7071067811865475 0.7071067811865476 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865475 0.7071067811865476 A 1 1 0 0 1 -1 1.2246467991473532e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1 1.2246467991473532e-16 A 1 1 0 0 1 -0.7071067811865477 -0.7071067811865475 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865477 -0.7071067811865475 A 1 1 0 0 1 -1.8369701987210297e-16 -1 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1.4672444368403393e-16 -0.7987306695894643 A 0.7987306695894643 0.7987306695894643 0 0 1 0.5647878728083817 -0.564787872808382 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5647878728083817 -0.564787872808382 A 0.7987306695894643 0.7987306695894643 0 0 1 0.7987306695894643 -1.9563259157871192e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7987306695894643 -1.9563259157871192e-16 A 0.7987306695894643 0.7987306695894643 0 0 1 0.5647878728083826 0.5647878728083813 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5647878728083826 0.5647878728083813 A 0.7987306695894643 0.7987306695894643 0 0 1 2.4454073947338993e-16 0.7987306695894643 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 2.4454073947338993e-16 0.7987306695894643 A 0.7987306695894643 0.7987306695894643 0 0 1 -0.5647878728083822 0.5647878728083816 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5647878728083822 0.5647878728083816 A 0.7987306695894643 0.7987306695894643 0 0 1 -0.7987306695894643 2.9344888736806786e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7987306695894643 2.9344888736806786e-16 A 0.7987306695894643 0.7987306695894643 0 0 1 -0.5647878728083826 -0.5647878728083812 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5647878728083826 -0.5647878728083812 A 0.7987306695894643 0.7987306695894643 0 0 1 -3.4235703526274585e-16 -0.7987306695894643 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -2.5159719038007387e-16 -0.5869848480983499 A 0.5869848480983499 0.5869848480983499 0 0 1 0.4150609665440989 -0.4150609665440986 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.4150609665440989 -0.4150609665440986 A 0.5869848480983499 0.5869848480983499 0 0 1 0.5869848480983499 -2.8753964614865583e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5869848480983499 -2.8753964614865583e-16 A 0.5869848480983499 0.5869848480983499 0 0 1 0.4150609665440994 0.4150609665440982 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.4150609665440994 0.4150609665440982 A 0.5869848480983499 0.5869848480983499 0 0 1 3.2348210191723783e-16 0.5869848480983499 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 3.2348210191723783e-16 0.5869848480983499 A 0.5869848480983499 0.5869848480983499 0 0 1 -0.41506096654409885 0.4150609665440987 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.41506096654409885 0.4150609665440987 A 0.5869848480983499 0.5869848480983499 0 0 1 -0.5869848480983499 1.4021191072296408e-15 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5869848480983499 1.4021191072296408e-15 A 0.5869848480983499 0.5869848480983499 0 0 1 -0.4150609665440987 -0.41506096654409885 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.4150609665440987 -0.41506096654409885 A 0.5869848480983499 0.5869848480983499 0 0 1 -1.4380615629982227e-15 -0.5869848480983499 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -8.697189655206095e-16 -0.355 A 0.355 0.355 0 0 1 0.25102290732122484 -0.25102290732122384 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.25102290732122484 -0.25102290732122384 A 0.355 0.355 0 0 1 0.355 -2.608497682183862e-16 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.355 -2.608497682183862e-16 A 0.355 0.355 0 0 1 0.25102290732122523 0.2510229073212235 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.25102290732122523 0.2510229073212235 A 0.355 0.355 0 0 1 -3.4801942908383715e-16 0.355 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -3.4801942908383715e-16 0.355 A 0.355 0.355 0 0 1 -0.25102290732122395 0.2510229073212248 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.25102290732122395 0.2510229073212248 A 0.355 0.355 0 0 1 -0.355 1.565538085562295e-15 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.355 1.565538085562295e-15 A 0.355 0.355 0 0 1 -0.2510229073212244 -0.2510229073212244 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.2510229073212244 -0.2510229073212244 A 0.355 0.355 0 0 1 -9.566688882600716e-16 -0.355 Z" fill="none" stroke="var(--colors-accent-500)" stroke-width="0.02" stroke-linejoin="round"></path></g><g><text x="0.34417221326942626" y="-0.800905225067014" text-anchor="middle" font-size="0.1" fill="currentColor">31</text><text x="0.830905225067014" y="-0.31417221326942624" text-anchor="middle" font-size="0.1" fill="currentColor">5f</text><text x="0.830905225067014" y="0.3741722132694262" text-anchor="middle" font-size="0.1" fill="currentColor">5b</text><text x="0.34417221326942626" y="0.8609052250670141" text-anchor="middle" font-size="0.1" fill="currentColor">db</text><text x="-0.34417221326942615" y="0.8609052250670141" text-anchor="middle" font-size="0.1" fill="currentColor">76</text><text x="-0.8309052250670138" y="0.3741722132694266" text-anchor="middle" font-size="0.1" fill="currentColor">d0</text><text x="-0.8309052250670139" y="-0.31417221326942646" text-anchor="middle" font-size="0.1" fill="currentColor">78</text><text x="-0.3441722132694267" y="-0.8009052250670138" text-anchor="middle" font-size="0.1" fill="currentColor">c4</text><text x="0.26514518529517017" y="-0.6101171023375266" text-anchor="middle" font-size="0.1" fill="currentColor">3b</text><text x="0.6401171023375266" y="-0.23514518529517045" text-anchor="middle" font-size="0.1" fill="currentColor">8a</text><text x="0.6401171023375267" y="0.2951451852951701" text-anchor="middle" font-size="0.1" fill="currentColor">c0</text><text x="0.26514518529517045" y="0.6701171023375266" text-anchor="middle" font-size="0.1" fill="currentColor">06</text><text x="-0.26514518529517006" y="0.6701171023375267" text-anchor="middle" font-size="0.1" fill="currentColor">4e</text><text x="-0.6401171023375265" y="0.29514518529517053" text-anchor="middle" font-size="0.1" fill="currentColor">4a</text><text x="-0.6401171023375267" y="-0.23514518529517006" text-anchor="middle" font-size="0.1" fill="currentColor">01</text><text x="-0.26514518529517056" y="-0.6101171023375265" text-anchor="middle" font-size="0.1" fill="currentColor">64</text><text x="0.18024099745309213" y="-0.40514026054690944" text-anchor="middle" font-size="0.1" fill="currentColor">61</text><text x="0.43514026054690935" y="-0.15024099745309252" text-anchor="middle" font-size="0.1" fill="currentColor">2b</text><text x="0.43514026054690946" y="0.2102409974530921" text-anchor="middle" font-size="0.1" fill="currentColor">1f</text><text x="0.18024099745309255" y="0.4651402605469094" text-anchor="middle" font-size="0.1" fill="currentColor">ce</text><text x="-0.1802409974530913" y="0.4651402605469098" text-anchor="middle" font-size="0.1" fill="currentColor">77</text><text x="-0.43514026054690935" y="0.21024099745309258" text-anchor="middle" font-size="0.1" fill="currentColor">c8</text><text x="-0.43514026054690924" y="-0.1502409974530928" text-anchor="middle" font-size="0.1" fill="currentColor">69</text><text x="-0.1802409974530926" y="-0.4051402605469093" text-anchor="middle" font-size="0.1" fill="currentColor">34</text><text x="0.08966272820313972" y="-0.18646497446739482" text-anchor="middle" font-size="0.1" fill="currentColor">5b</text><text x="0.21646497446739454" y="-0.059662728203140405" text-anchor="middle" font-size="0.1" fill="currentColor">fc</text><text x="0.21646497446739468" y="0.11966272820314008" text-anchor="middle" font-size="0.1" fill="currentColor">94</text><text x="0.08966272820314003" y="0.2464649744673947" text-anchor="middle" font-size="0.1" fill="currentColor">c7</text><text x="-0.08966272820314046" y="0.24646497446739454" text-anchor="middle" font-size="0.1" fill="currentColor">58</text><text x="-0.21646497446739424" y="0.11966272820314121" text-anchor="middle" font-size="0.1" fill="currentColor">94</text><text x="-0.21646497446739438" y="-0.05966272820314082" text-anchor="middle" font-size="0.1" fill="currentColor">ed</text><text x="-0.08966272820314082" y="-0.18646497446739438" text-anchor="middle" font-size="0.1" fill="currentColor">d3</text></g></svg><p class="chakra-text css-8fkoyy">In order to fill each section with a different colour, we generate an SVG <code class="chakra-code css-1uwjdr8"><path></code> polygon.
- Each section resembles a pie/pizza slice, going from the center
- of the circle to a given radius, and covering 1/8th of the circle.</p><div role="alert" class="chakra-alert css-xjse24"><p class="chakra-alert__desc css-12db8z9">The reason we can get away with all sections going to the center is because of our mapping order: by laying out from the outside in, the inner sections will be overlaid on top of the outer ones in z-index.</p></div><p class="chakra-text css-8fkoyy">Since SVG uses cartesian coordinates, we'll have to convert our polar logic
- into cartesian SVG path commands:</p><pre class="language-tsx"><code class="language-tsx{31,32,33,34,35,36}"><span class="token keyword">interface</span> <span class="token class-name">Point</span> <span class="token punctuation">{</span>
- x<span class="token punctuation">:</span> <span class="token builtin">number</span>
- y<span class="token punctuation">:</span> <span class="token builtin">number</span>
- <span class="token punctuation">}</span></p>
- <p><span class="token keyword">function</span> <span class="token function">polarPoint</span><span class="token punctuation">(</span><span class="token parameter">radius<span class="token punctuation">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> angle<span class="token punctuation">:</span> <span class="token builtin">number</span></span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token maybe-class-name">Point</span> <span class="token punctuation">{</span></p>
- <p><span class="token keyword">return</span> <span class="token punctuation">{</span>
- x<span class="token punctuation">:</span> radius <span class="token operator"><em></span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">cos</span><span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator"></em></span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator"><em></span> angle <span class="token operator">-</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
- y<span class="token punctuation">:</span> radius <span class="token operator"></em></span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">sin</span><span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator"><em></span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator"></em></span> angle <span class="token operator">-</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">)</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span></p>
- <p><span class="token keyword">function</span> <span class="token function">moveTo</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> x<span class="token punctuation">,</span> y <span class="token punctuation">}</span><span class="token punctuation">:</span> <span class="token maybe-class-name">Point</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code></span><span class="token string">M </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>x<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>y<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string"></code></span></span>
- <span class="token punctuation">}</span></p>
- <p><span class="token keyword">function</span> <span class="token function">lineTo</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> x<span class="token punctuation">,</span> y <span class="token punctuation">}</span><span class="token punctuation">:</span> <span class="token maybe-class-name">Point</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code></span><span class="token string">L </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>x<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>y<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string"></code></span></span>
- <span class="token punctuation">}</span></p>
- <p><span class="token keyword">function</span> <span class="token function">arcTo</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> x<span class="token punctuation">,</span> y <span class="token punctuation">}</span><span class="token punctuation">:</span> <span class="token maybe-class-name">Point</span><span class="token punctuation">,</span> radius<span class="token punctuation">:</span> <span class="token builtin">number</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code></span><span class="token string">A </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>radius<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>radius<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> 0 0 0 </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>x<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>y<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string"></code></span></span>
- <span class="token punctuation">}</span></p>
- <p><span class="token keyword">const</span> <span class="token function-variable function"><span class="token maybe-class-name">Section</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> index<span class="token punctuation">,</span> radius <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span>
- <span class="token keyword">const</span> angleA <span class="token operator">=</span> index <span class="token operator">/</span> <span class="token number">8</span>
- <span class="token keyword">const</span> angleB <span class="token operator">=</span> <span class="token punctuation">(</span>index <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">8</span>
- <p class="mdx-marker"> <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token punctuation">[</span>
- </p><p class="mdx-marker"> <span class="token function">moveTo</span><span class="token punctuation">(</span><span class="token punctuation">{</span> x<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> y<span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
- </p><p class="mdx-marker"> <span class="token function">lineTo</span><span class="token punctuation">(</span><span class="token function">polarPoint</span><span class="token punctuation">(</span>radius<span class="token punctuation">,</span> angleA<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
- </p><p class="mdx-marker"> <span class="token function">arcTo</span><span class="token punctuation">(</span><span class="token function">polarPoint</span><span class="token punctuation">(</span>radius<span class="token punctuation">,</span> angleB<span class="token punctuation">)</span><span class="token punctuation">,</span> radius<span class="token punctuation">)</span><span class="token punctuation">,</span>
- </p><p class="mdx-marker"> <span class="token string">'Z'</span>
- </p><p class="mdx-marker"> <span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token method function property-access">join</span><span class="token punctuation">(</span><span class="token string">' '</span><span class="token punctuation">)</span>
- </p> <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>path<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span>
- <span class="token punctuation">}</span>
- </code></pre><h2 class="chakra-heading css-1rjvsw4" id="colour-mapping">Colour Mapping<a class="chakra-link css-jchjfc" aria-label="anchor" href="#colour-mapping">#</a></h2><p class="chakra-text css-8fkoyy">Now we can turn each section's byte value into a colour.</p><p class="chakra-text css-8fkoyy">For that, we have many options. 8 bits could map directly to 256 colours like the
- old Windows systems, but that would require a lookup table. Instead, we can
- generate colours using the <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl()"><code class="chakra-code css-1uwjdr8">hsl()</code></a> CSS function.</p><p class="chakra-text css-8fkoyy">Hue is the component that has the most visual impact, while Saturation and
- Lightness can be used to add little variants to each section.</p><p class="chakra-text css-8fkoyy">To map our 8 bit value to 3 components, we can divide the byte into:</p><ul role="list" class="css-80rt2u"><li class="css-77vupy">4 bits for the Hue (16 values)</li><li class="css-77vupy">2 bits for the Saturation (4 values)</li><li class="css-77vupy">2 bits for the Lightness (4 values)</li></ul><pre class="language-ts"><code class="language-ts"><span class="token keyword">function</span> <span class="token function">mapValueToColor</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">const</span> colorH <span class="token operator">=</span> value <span class="token operator">>></span> <span class="token number">4</span>
- <span class="token keyword">const</span> colorS <span class="token operator">=</span> <span class="token punctuation">(</span>value <span class="token operator">>></span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">&</span> <span class="token number">0x03</span>
- <span class="token keyword">const</span> colorL <span class="token operator">=</span> value <span class="token operator">&</span> <span class="token number">0x03</span>
- <span class="token keyword">const</span> normalizedH <span class="token operator">=</span> colorH <span class="token operator">/</span> <span class="token number">16</span>
- <span class="token keyword">const</span> normalizedS <span class="token operator">=</span> colorS <span class="token operator">/</span> <span class="token number">4</span>
- <span class="token keyword">const</span> normalizedL <span class="token operator">=</span> colorL <span class="token operator">/</span> <span class="token number">4</span>
- <span class="token keyword">const</span> h <span class="token operator">=</span> <span class="token number">360</span> <span class="token operator"><em></span> normalizedH
- <span class="token keyword">const</span> s <span class="token operator">=</span> <span class="token number">50</span> <span class="token operator">+</span> <span class="token number">50</span> <span class="token operator"></em></span> normalizedS
- <span class="token keyword">const</span> l <span class="token operator">=</span> <span class="token number">40</span> <span class="token operator">+</span> <span class="token number">30</span> <span class="token operator"><em></span> normalizedL
- <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code></span><span class="token string">hsl(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>h<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>s<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">%, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>l<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">%)</span><span class="token template-punctuation string"></code></span></span>
- <span class="token punctuation">}</span>
- </code></pre><p class="chakra-text css-8fkoyy">We can adjust the range for each component to get nice results:</p><svg xmlns="http://www.w3.org/2000/svg" role="img" viewbox="-1 -1 2 2" class="css-q1xci"><g><path d="M 0 0 L 6.123233995736766e-17 -1 A 1 1 0 0 1 0.7071067811865476 -0.7071067811865475 Z" fill="hsl(67.5, 50%, 47.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 -0.7071067811865475 A 1 1 0 0 1 1 0 Z" fill="hsl(112.5, 87.5%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 1 0 A 1 1 0 0 1 0.7071067811865476 0.7071067811865475 Z" fill="hsl(112.5, 75%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7071067811865476 0.7071067811865475 A 1 1 0 0 1 6.123233995736766e-17 1 Z" fill="hsl(292.5, 75%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 6.123233995736766e-17 1 A 1 1 0 0 1 -0.7071067811865475 0.7071067811865476 Z" fill="hsl(157.5, 62.5%, 55%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865475 0.7071067811865476 A 1 1 0 0 1 -1 1.2246467991473532e-16 Z" fill="hsl(292.5, 50%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1 1.2246467991473532e-16 A 1 1 0 0 1 -0.7071067811865477 -0.7071067811865475 Z" fill="hsl(157.5, 75%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7071067811865477 -0.7071067811865475 A 1 1 0 0 1 -1.8369701987210297e-16 -1 Z" fill="hsl(270, 62.5%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -1.4672444368403393e-16 -0.7987306695894643 A 0.7987306695894643 0.7987306695894643 0 0 1 0.5647878728083817 -0.564787872808382 Z" fill="hsl(67.5, 75%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5647878728083817 -0.564787872808382 A 0.7987306695894643 0.7987306695894643 0 0 1 0.7987306695894643 -1.9563259157871192e-16 Z" fill="hsl(180, 75%, 55%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.7987306695894643 -1.9563259157871192e-16 A 0.7987306695894643 0.7987306695894643 0 0 1 0.5647878728083826 0.5647878728083813 Z" fill="hsl(270, 50%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5647878728083826 0.5647878728083813 A 0.7987306695894643 0.7987306695894643 0 0 1 2.4454073947338993e-16 0.7987306695894643 Z" fill="hsl(0, 62.5%, 55%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 2.4454073947338993e-16 0.7987306695894643 A 0.7987306695894643 0.7987306695894643 0 0 1 -0.5647878728083822 0.5647878728083816 Z" fill="hsl(90, 87.5%, 55%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5647878728083822 0.5647878728083816 A 0.7987306695894643 0.7987306695894643 0 0 1 -0.7987306695894643 2.9344888736806786e-16 Z" fill="hsl(90, 75%, 55%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.7987306695894643 2.9344888736806786e-16 A 0.7987306695894643 0.7987306695894643 0 0 1 -0.5647878728083826 -0.5647878728083812 Z" fill="hsl(0, 50%, 47.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5647878728083826 -0.5647878728083812 A 0.7987306695894643 0.7987306695894643 0 0 1 -3.4235703526274585e-16 -0.7987306695894643 Z" fill="hsl(135, 62.5%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -2.5159719038007387e-16 -0.5869848480983499 A 0.5869848480983499 0.5869848480983499 0 0 1 0.4150609665440989 -0.4150609665440986 Z" fill="hsl(135, 50%, 47.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.4150609665440989 -0.4150609665440986 A 0.5869848480983499 0.5869848480983499 0 0 1 0.5869848480983499 -2.8753964614865583e-16 Z" fill="hsl(45, 75%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.5869848480983499 -2.8753964614865583e-16 A 0.5869848480983499 0.5869848480983499 0 0 1 0.4150609665440994 0.4150609665440982 Z" fill="hsl(22.5, 87.5%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.4150609665440994 0.4150609665440982 A 0.5869848480983499 0.5869848480983499 0 0 1 3.2348210191723783e-16 0.5869848480983499 Z" fill="hsl(270, 87.5%, 55%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 3.2348210191723783e-16 0.5869848480983499 A 0.5869848480983499 0.5869848480983499 0 0 1 -0.41506096654409885 0.4150609665440987 Z" fill="hsl(157.5, 62.5%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.41506096654409885 0.4150609665440987 A 0.5869848480983499 0.5869848480983499 0 0 1 -0.5869848480983499 1.4021191072296408e-15 Z" fill="hsl(270, 75%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.5869848480983499 1.4021191072296408e-15 A 0.5869848480983499 0.5869848480983499 0 0 1 -0.4150609665440987 -0.41506096654409885 Z" fill="hsl(135, 75%, 47.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.4150609665440987 -0.41506096654409885 A 0.5869848480983499 0.5869848480983499 0 0 1 -1.4380615629982227e-15 -0.5869848480983499 Z" fill="hsl(67.5, 62.5%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -8.697189655206095e-16 -0.355 A 0.355 0.355 0 0 1 0.25102290732122484 -0.25102290732122384 Z" fill="hsl(112.5, 75%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.25102290732122484 -0.25102290732122384 A 0.355 0.355 0 0 1 0.355 -2.608497682183862e-16 Z" fill="hsl(337.5, 87.5%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.355 -2.608497682183862e-16 A 0.355 0.355 0 0 1 0.25102290732122523 0.2510229073212235 Z" fill="hsl(202.5, 62.5%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L 0.25102290732122523 0.2510229073212235 A 0.355 0.355 0 0 1 -3.4801942908383715e-16 0.355 Z" fill="hsl(270, 62.5%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -3.4801942908383715e-16 0.355 A 0.355 0.355 0 0 1 -0.25102290732122395 0.2510229073212248 Z" fill="hsl(112.5, 75%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.25102290732122395 0.2510229073212248 A 0.355 0.355 0 0 1 -0.355 1.565538085562295e-15 Z" fill="hsl(202.5, 62.5%, 40%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.355 1.565538085562295e-15 A 0.355 0.355 0 0 1 -0.2510229073212244 -0.2510229073212244 Z" fill="hsl(315, 87.5%, 47.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path><path d="M 0 0 L -0.2510229073212244 -0.2510229073212244 A 0.355 0.355 0 0 1 -9.566688882600716e-16 -0.355 Z" fill="hsl(292.5, 50%, 62.5%)" stroke="white" stroke-width="0.02" stroke-linejoin="round"></path></g></svg><div role="alert" class="chakra-alert css-7l7mra"><p class="chakra-alert__desc css-12db8z9">The colour mapping function could use a high-contrast version that focuses on the Luminosity channel rather than the Hue.</p></div><h3 class="chakra-heading css-106yvz" id="order-chaos--soul">Order, Chaos & Soul<a class="chakra-link css-jchjfc" aria-label="anchor" href="#order-chaos--soul">#</a></h3><p class="chakra-text css-8fkoyy">Our colour encoding suffers from a flaw: two hashes can look very similar, but
- have a few bits of difference here and there. They can go unnoticed especially
- if differences occur in the LSBs of hue/saturation/lightness components.</p><p class="chakra-text css-8fkoyy">Also, the sections look random in colour, and the whole avatar lacks coherence.</p><p class="chakra-text css-8fkoyy">It would be nice if there was some pattern to a hash that makes it
- random enough to be distinguished yet coherent enough within itself.
- A balance between order and chaos.</p><p class="chakra-text css-8fkoyy">In order to fix that, we compute the <strong class="chakra-text css-35ezg3">soul</strong> of the hash, using XOR operations.</p><ol role="list" class="css-h1hn4m"><li class="css-77vupy">We XOR all the bytes of the hash together to compute the <strong class="chakra-text css-35ezg3">hash soul</strong></li><li class="css-77vupy">For each ring, we XOR the bytes that form this ring's section to compute the <strong class="chakra-text css-35ezg3">ring's soul</strong>. <em><a class="chakra-link css-y3jojq" href="/horcrux">(horcruxes?)</a></em></li></ol><pre class="language-ts"><code class="language-ts">
- <span class="token keyword">function</span> <span class="token function">computeSouls</span><span class="token punctuation">(</span><span class="token parameter">bytes<span class="token punctuation">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">const</span> ringLength <span class="token operator">=</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">round</span><span class="token punctuation">(</span>bytes<span class="token punctuation">.</span><span class="token property-access">length</span> <span class="token operator">/</span> <span class="token number">4</span><span class="token punctuation">)</span>
- <span class="token keyword">const</span> rings <span class="token operator">=</span> <span class="token punctuation">[</span>
- bytes<span class="token punctuation">.</span><span class="token method function property-access">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> ringLength<span class="token punctuation">)</span><span class="token punctuation">,</span>
- bytes<span class="token punctuation">.</span><span class="token method function property-access">slice</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator"></em></span> ringLength<span class="token punctuation">,</span> <span class="token number">2</span> <span class="token operator"><em></span> ringLength<span class="token punctuation">)</span><span class="token punctuation">,</span>
- bytes<span class="token punctuation">.</span><span class="token method function property-access">slice</span><span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator"></em></span> ringLength<span class="token punctuation">,</span> <span class="token number">3</span> <span class="token operator"><em></span> ringLength<span class="token punctuation">)</span><span class="token punctuation">,</span>
- bytes<span class="token punctuation">.</span><span class="token method function property-access">slice</span><span class="token punctuation">(</span><span class="token number">3</span> <span class="token operator"></em></span> ringLength<span class="token punctuation">,</span> <span class="token number">4</span> <span class="token operator"><em></span> ringLength<span class="token punctuation">)</span>
- <span class="token punctuation">]</span>
- <span class="token keyword">const</span> <span class="token function-variable function">xorReducer</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">xor<span class="token punctuation">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> byte<span class="token punctuation">:</span> <span class="token builtin">string</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> xor <span class="token operator">^</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>byte<span class="token punctuation">,</span> <span class="token number">16</span><span class="token punctuation">)</span>
- <span class="token keyword">return</span> <span class="token punctuation">{</span>
- hashSoul<span class="token punctuation">:</span> <span class="token punctuation">(</span>bytes<span class="token punctuation">.</span><span class="token method function property-access">reduce</span><span class="token punctuation">(</span>xorReducer<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">0xff</span><span class="token punctuation">)</span> <span class="token operator"></em></span> <span class="token number">2</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span>
- ringSouls<span class="token punctuation">:</span> rings<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token parameter">ring</span> <span class="token arrow operator">=></span> <span class="token punctuation">(</span>ring<span class="token punctuation">.</span><span class="token method function property-access">reduce</span><span class="token punctuation">(</span>xorReducer<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">0xff</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">2</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span></p>
- <p></code></pre><p class="chakra-text css-8fkoyy">These values give us additional parameters to play with in the colour calculation.</p><p class="chakra-text css-8fkoyy">Notably, we can <em>"seed"</em> the Hue with the hash soul, and introduce hue varitions
- per-ring with each ring soul, and with the value itself.</p><pre class="language-ts"><code class="language-ts{8,9,10}"><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">mapValueToColor</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> value<span class="token punctuation">,</span> hashSoul<span class="token punctuation">,</span> ringSoul <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">const</span> colorH <span class="token operator">=</span> value <span class="token operator">>></span> <span class="token number">4</span>
- <span class="token keyword">const</span> colorS <span class="token operator">=</span> <span class="token punctuation">(</span>value <span class="token operator">>></span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">&</span> <span class="token number">0x03</span>
- <span class="token keyword">const</span> colorL <span class="token operator">=</span> value <span class="token operator">&</span> <span class="token number">0x03</span>
- <span class="token keyword">const</span> normalizedH <span class="token operator">=</span> colorH <span class="token operator">/</span> <span class="token number">16</span>
- <span class="token keyword">const</span> normalizedS <span class="token operator">=</span> colorS <span class="token operator">/</span> <span class="token number">4</span>
- <span class="token keyword">const</span> normalizedL <span class="token operator">=</span> colorL <span class="token operator">/</span> <span class="token number">4</span>
- <p class="mdx-marker"> <span class="token keyword">const</span> h <span class="token operator">=</span> <span class="token number">360</span> <span class="token operator"><em></span> hashSoul
- </p><p class="mdx-marker"> <span class="token operator">+</span> <span class="token number">120</span> <span class="token operator"></em></span> ringSoul
- </p><p class="mdx-marker"> <span class="token operator">+</span> <span class="token number">30</span> <span class="token operator"><em></span> normalizedH
- </p> <span class="token keyword">const</span> s <span class="token operator">=</span> <span class="token number">50</span> <span class="token operator">+</span> <span class="token number">50</span> <span class="token operator"></em></span> normalizedS
- <span class="token keyword">const</span> l <span class="token operator">=</span> <span class="token number">40</span> <span class="token operator">+</span> <span class="token number">30</span> <span class="token operator">*</span> normalizedL
- <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code></span><span class="token string">hsl(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>h<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>s<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">%, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>l<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">%)</span><span class="token template-punctuation string"></code></span></span>
- <span class="token punctuation">}</span>
- </code></pre><p class="chakra-text css-8fkoyy">We can also introduce structural variations by changing each ring's starting
- angle based on the ring soul, to create a staggering effect:</p><div class="css-8fkoyy"><div class="css-1u4m77"><p class="chakra-text css-1fz97de">Without souls</p><p class="chakra-text css-1fz97de">With souls</p><p class="chakra-text css-1fz97de">With souls & staggering</p></div></div><p class="chakra-text css-8fkoyy">Not only does this help give a bit more uniqueness to the avatar, it also helps
- with accessibility for colour-blind people</p><h2 class="chakra-heading css-1rjvsw4" id="a-bit-of-fun">A Bit Of Fun<a class="chakra-link css-jchjfc" aria-label="anchor" href="#a-bit-of-fun">#</a></h2><p class="chakra-text css-8fkoyy">If we change the radius and flags for the arc part of the section paths and
- play with each ring's starting angle, we can obtain interesting variations:</p><h2 class="chakra-heading css-1rjvsw4" id="going-further">Going Further<a class="chakra-link css-jchjfc" aria-label="anchor" href="#going-further">#</a></h2><p class="chakra-text css-8fkoyy">With a bit of tweaking in the colour mapping value, we can easily extend this
- technique to arbitrary hash lengths (as long as said length is divisible by 32).</p><p class="chakra-text css-8fkoyy">It so happens that when I started this project, Centralized Coin was using SHA-256,
- but later on switched to SHA-384, which gives 12 bits per section.</p><h2 class="chakra-heading css-1rjvsw4" id="conclusion">Conclusion<a class="chakra-link css-jchjfc" aria-label="anchor" href="#conclusion">#</a></h2><p class="chakra-text css-8fkoyy">You can see the hashvatars <em>(thanks to <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://twitter.com/wzulfikar">@wzulfikar</a> for the name)</em>
- in action in the
- <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://centralized-coin-explorer.vercel.app">Centralized Coin Explorer</a>,
- or play with the variants yourself <a class="chakra-link css-y3jojq" href="/hashvatar">on the playground</a>.</p><p class="chakra-text css-8fkoyy">I will publish the code as an NPM package later, in the mean time the source
- <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://github.com/franky47/francoisbest.com/blob/next/src/pages/posts/2021/hashvatars.mdx">code for this article</a>
- is on GitHub, as well as the
- <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://github.com/franky47/francoisbest.com/blob/next/src/components/SHA256Avatar.tsx">component itself</a>.</p><p class="chakra-text css-8fkoyy"><a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://twitter.com/fortysevenfx">Follow me on Twitter</a> for updates and more weekend projects.</p></p>
- </article>
-
-
- <hr>
-
- <footer>
- <p>
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
- </svg> RSS</a> •
- <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
- </svg> Pro</a> •
- <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
- </svg> Email</a> •
- <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
- </svg> Légal</abbr>
- </p>
- <template id="theme-selector">
- <form>
- <fieldset>
- <legend><svg class="icon icon-brightness-contrast">
- <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
- </svg> Thème</legend>
- <label>
- <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
- </label>
- <label>
- <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
- </label>
- <label>
- <input type="radio" value="light" name="chosen-color-scheme"> Clair
- </label>
- </fieldset>
- </form>
- </template>
- </footer>
- <script>
- function loadThemeForm(templateName) {
- const themeSelectorTemplate = document.querySelector(templateName)
- const form = themeSelectorTemplate.content.firstElementChild
- themeSelectorTemplate.replaceWith(form)
-
- form.addEventListener('change', (e) => {
- const chosenColorScheme = e.target.value
- localStorage.setItem('theme', chosenColorScheme)
- toggleTheme(chosenColorScheme)
- })
-
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme && selectedTheme !== 'undefined') {
- form.querySelector(`[value="${selectedTheme}"]`).checked = true
- }
- }
-
- const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
- window.addEventListener('load', () => {
- let hasDarkRules = false
- for (const styleSheet of Array.from(document.styleSheets)) {
- let mediaRules = []
- for (const cssRule of styleSheet.cssRules) {
- if (cssRule.type !== CSSRule.MEDIA_RULE) {
- continue
- }
- // WARNING: Safari does not have/supports `conditionText`.
- if (cssRule.conditionText) {
- if (cssRule.conditionText !== prefersColorSchemeDark) {
- continue
- }
- } else {
- if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
- continue
- }
- }
- mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
- }
-
- // WARNING: do not try to insert a Rule to a styleSheet you are
- // currently iterating on, otherwise the browser will be stuck
- // in a infinite loop…
- for (const mediaRule of mediaRules) {
- styleSheet.insertRule(mediaRule.cssText)
- hasDarkRules = true
- }
- }
- if (hasDarkRules) {
- loadThemeForm('#theme-selector')
- }
- })
- </script>
- </body>
- </html>
|