A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 86KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Representing SHA-256 Hashes As Avatars (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f0f0ea">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f0f0ea">
  26. <!-- Documented, feel free to shoot an email. -->
  27. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  28. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  29. <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>
  30. <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>
  31. <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>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <script>
  36. function toggleTheme(themeName) {
  37. document.documentElement.classList.toggle(
  38. 'forced-dark',
  39. themeName === 'dark'
  40. )
  41. document.documentElement.classList.toggle(
  42. 'forced-light',
  43. themeName === 'light'
  44. )
  45. }
  46. const selectedTheme = localStorage.getItem('theme')
  47. if (selectedTheme !== 'undefined') {
  48. toggleTheme(selectedTheme)
  49. }
  50. </script>
  51. <meta name="robots" content="noindex, nofollow">
  52. <meta content="origin-when-cross-origin" name="referrer">
  53. <!-- Canonical URL for SEO purposes -->
  54. <link rel="canonical" href="https://francoisbest.com/posts/2021/hashvatars">
  55. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Representing SHA-256 Hashes As Avatars</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  63. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  64. </svg> Accueil</a> •
  65. <a href="https://francoisbest.com/posts/2021/hashvatars" title="Lien vers le contenu original">Source originale</a>
  66. </p>
  67. </nav>
  68. <hr>
  69. <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
  70. 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>
  71. <p class="chakra-text css-8fkoyy">They are little challenges I give myself, usually without too many stakes
  72. involved, and with small enough a scope so that I can ship it in a day or two,
  73. while keeping spare family time.</p>
  74. <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>
  75. experiment. Something like a blockchain explorer, but without the crypto overhead.</p>
  76. <p class="chakra-text css-8fkoyy">Each node in the system is represented by a hash. Because humans are
  77. terrible at reading and quickly identifying large numbers (other than by their
  78. first or last few digits), a visual representation is needed.</p>
  79. <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>
  80. 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>
  81. <p class="chakra-text css-8fkoyy">I wanted one that still looks abstract (not as opinionated as monsters or robots),
  82. and that plays nice in a rounded avatar UI component.</p>
  83. <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>
  84. <p class="chakra-text css-8fkoyy">If you are allergic to maths and trigonometry, feel free to play with the more
  85. detailed <a class="chakra-link css-y3jojq" href="/hashvatar">interactive example here</a>. Otherwise, let's dive into
  86. how it's done.</p>
  87. <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>
  88. <p class="chakra-text css-8fkoyy">Many of the existing solutions produce square images, yet avatars are often
  89. displayed as circles. They would lose information on the corners when rounded,
  90. so instead of a cartesian (x-y) approach, we're going to use polar (angle-radius)
  91. coordinates instead.</p>
  92. <p class="chakra-text css-8fkoyy">A grid in cartesian space maps to concentric circles and pie-like cuts in
  93. polar space.</p>
  94. <p class="chakra-text css-8fkoyy">SHA-256 hashes have 256 bits of information that we need to represent. Dividing
  95. a circle into 256 sections would make each section too small to be visually useful,
  96. and would only leave 1 bit of "value" to represent in each section
  97. (0 or 1, black or white).</p>
  98. <p class="chakra-text css-8fkoyy">Instead, we're going to divide the circle into 32 sections:</p>
  99. <ul role="list" class="css-80rt2u"><li class="css-77vupy">4 concentric rings</li><li class="css-77vupy">8 pie-like cuts</li></ul>
  100. <p class="chakra-text css-8fkoyy">The resulting SVG code for such a grid looks like this (in a React component):</p>
  101. <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">=&gt;</span> <span class="token punctuation">{</span>
  102. <span class="token keyword">const</span> r1 <span class="token operator">=</span> <span class="token number">1</span>
  103. <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>
  104. <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>
  105. <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>
  106. <span class="token keyword">return</span> <span class="token punctuation">(</span>
  107. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&gt;</span></span><span class="token plain-text">
  108. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span><span class="token plain-text">
  109. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span><span class="token plain-text">
  110. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span><span class="token plain-text">
  111. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span><span class="token plain-text">
  112. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span><span class="token plain-text">
  113. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span><span class="token plain-text">
  114. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>line</span>
  115. <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>
  116. <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>
  117. <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>
  118. <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>
  119. <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
  120. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>line</span>
  121. <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>
  122. <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>
  123. <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>
  124. <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>
  125. <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
  126. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">&gt;</span></span>
  127. <span class="token punctuation">)</span>
  128. <span class="token punctuation">}</span>
  129. </code></pre>
  130. <p class="chakra-text css-8fkoyy">Doing this naively, with each concentric ring's radius being 1/4th of the
  131. outermost/largest one gives us this:</p>
  132. <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
  133. 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
  134. <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
  135. 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
  136. </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
  137. 4 blocks of 8 to map to the rings:</p><pre><code>12 o'clock -&gt; clockwise
  138. 31 5f 5b db 76 d0 78 c4 Outer ring
  139. 3b 8a c0 06 4e 4a 01 64 Middle-outer ring
  140. 61 2b 1f ce 77 c8 69 34 Middle-inner ring
  141. 5b fc 94 c7 58 94 ed d3 Inner ring
  142. </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">&lt;path&gt;</code> polygon.
  143. Each section resembles a pie/pizza slice, going from the center
  144. 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
  145. 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>
  146. x<span class="token punctuation">:</span> <span class="token builtin">number</span>
  147. y<span class="token punctuation">:</span> <span class="token builtin">number</span>
  148. <span class="token punctuation">}</span></p>
  149. <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>
  150. <p><span class="token keyword">return</span> <span class="token punctuation">{</span>
  151. 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>
  152. 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>
  153. <span class="token punctuation">}</span>
  154. <span class="token punctuation">}</span></p>
  155. <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>
  156. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code>&lt;/span&gt;&lt;span class="token string"&gt;M &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;x&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt; &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;y&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token template-punctuation string"&gt;</code></span></span>
  157. <span class="token punctuation">}</span></p>
  158. <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>
  159. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code>&lt;/span&gt;&lt;span class="token string"&gt;L &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;x&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt; &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;y&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token template-punctuation string"&gt;</code></span></span>
  160. <span class="token punctuation">}</span></p>
  161. <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>
  162. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code>&lt;/span&gt;&lt;span class="token string"&gt;A &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;radius&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt; &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;radius&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt; 0 0 0 &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;x&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt; &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;y&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token template-punctuation string"&gt;</code></span></span>
  163. <span class="token punctuation">}</span></p>
  164. <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">=&gt;</span> <span class="token punctuation">{</span>
  165. <span class="token keyword">const</span> angleA <span class="token operator">=</span> index <span class="token operator">/</span> <span class="token number">8</span>
  166. <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>
  167. <p class="mdx-marker"> <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token punctuation">[</span>
  168. </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>
  169. </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>
  170. </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>
  171. </p><p class="mdx-marker"> <span class="token string">'Z'</span>
  172. </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>
  173. </p> <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">/&gt;</span></span>
  174. <span class="token punctuation">}</span>
  175. </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
  176. old Windows systems, but that would require a lookup table. Instead, we can
  177. 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
  178. 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>
  179. <span class="token keyword">const</span> colorH <span class="token operator">=</span> value <span class="token operator">&gt;&gt;</span> <span class="token number">4</span>
  180. <span class="token keyword">const</span> colorS <span class="token operator">=</span> <span class="token punctuation">(</span>value <span class="token operator">&gt;&gt;</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">&amp;</span> <span class="token number">0x03</span>
  181. <span class="token keyword">const</span> colorL <span class="token operator">=</span> value <span class="token operator">&amp;</span> <span class="token number">0x03</span>
  182. <span class="token keyword">const</span> normalizedH <span class="token operator">=</span> colorH <span class="token operator">/</span> <span class="token number">16</span>
  183. <span class="token keyword">const</span> normalizedS <span class="token operator">=</span> colorS <span class="token operator">/</span> <span class="token number">4</span>
  184. <span class="token keyword">const</span> normalizedL <span class="token operator">=</span> colorL <span class="token operator">/</span> <span class="token number">4</span>
  185. <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
  186. <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
  187. <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
  188. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code>&lt;/span&gt;&lt;span class="token string"&gt;hsl(&lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;h&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt;, &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;s&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt;%, &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;l&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt;%)&lt;/span&gt;&lt;span class="token template-punctuation string"&gt;</code></span></span>
  189. <span class="token punctuation">}</span>
  190. </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 &amp; 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
  191. have a few bits of difference here and there. They can go unnoticed especially
  192. 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
  193. random enough to be distinguished yet coherent enough within itself.
  194. 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">
  195. <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>
  196. <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>
  197. <span class="token keyword">const</span> rings <span class="token operator">=</span> <span class="token punctuation">[</span>
  198. 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>
  199. 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>
  200. 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>
  201. 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>
  202. <span class="token punctuation">]</span>
  203. <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">=&gt;</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>
  204. <span class="token keyword">return</span> <span class="token punctuation">{</span>
  205. 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>
  206. 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">=&gt;</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>
  207. <span class="token punctuation">}</span>
  208. <span class="token punctuation">}</span></p>
  209. <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
  210. 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>
  211. <span class="token keyword">const</span> colorH <span class="token operator">=</span> value <span class="token operator">&gt;&gt;</span> <span class="token number">4</span>
  212. <span class="token keyword">const</span> colorS <span class="token operator">=</span> <span class="token punctuation">(</span>value <span class="token operator">&gt;&gt;</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">&amp;</span> <span class="token number">0x03</span>
  213. <span class="token keyword">const</span> colorL <span class="token operator">=</span> value <span class="token operator">&amp;</span> <span class="token number">0x03</span>
  214. <span class="token keyword">const</span> normalizedH <span class="token operator">=</span> colorH <span class="token operator">/</span> <span class="token number">16</span>
  215. <span class="token keyword">const</span> normalizedS <span class="token operator">=</span> colorS <span class="token operator">/</span> <span class="token number">4</span>
  216. <span class="token keyword">const</span> normalizedL <span class="token operator">=</span> colorL <span class="token operator">/</span> <span class="token number">4</span>
  217. <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
  218. </p><p class="mdx-marker"> <span class="token operator">+</span> <span class="token number">120</span> <span class="token operator"></em></span> ringSoul
  219. </p><p class="mdx-marker"> <span class="token operator">+</span> <span class="token number">30</span> <span class="token operator"><em></span> normalizedH
  220. </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
  221. <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
  222. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string"><code>&lt;/span&gt;&lt;span class="token string"&gt;hsl(&lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;h&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt;, &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;s&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt;%, &lt;/span&gt;&lt;span class="token interpolation"&gt;&lt;span class="token interpolation-punctuation punctuation"&gt;${&lt;/span&gt;l&lt;span class="token interpolation-punctuation punctuation"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="token string"&gt;%)&lt;/span&gt;&lt;span class="token template-punctuation string"&gt;</code></span></span>
  223. <span class="token punctuation">}</span>
  224. </code></pre><p class="chakra-text css-8fkoyy">We can also introduce structural variations by changing each ring's starting
  225. 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 &amp; 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
  226. 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
  227. 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
  228. 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,
  229. 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>
  230. in action in the
  231. <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://centralized-coin-explorer.vercel.app">Centralized Coin Explorer</a>,
  232. 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
  233. <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>
  234. is on GitHub, as well as the
  235. <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>
  236. </article>
  237. <hr>
  238. <footer>
  239. <p>
  240. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  241. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  242. </svg> Accueil</a> •
  243. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  244. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
  245. </svg> RSS</a> •
  246. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  247. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
  248. </svg> Pro</a> •
  249. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  250. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
  251. </svg> Email</a> •
  252. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  253. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
  254. </svg> Légal</abbr>
  255. </p>
  256. <template id="theme-selector">
  257. <form>
  258. <fieldset>
  259. <legend><svg class="icon icon-brightness-contrast">
  260. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
  261. </svg> Thème</legend>
  262. <label>
  263. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  264. </label>
  265. <label>
  266. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  267. </label>
  268. <label>
  269. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  270. </label>
  271. </fieldset>
  272. </form>
  273. </template>
  274. </footer>
  275. <script>
  276. function loadThemeForm(templateName) {
  277. const themeSelectorTemplate = document.querySelector(templateName)
  278. const form = themeSelectorTemplate.content.firstElementChild
  279. themeSelectorTemplate.replaceWith(form)
  280. form.addEventListener('change', (e) => {
  281. const chosenColorScheme = e.target.value
  282. localStorage.setItem('theme', chosenColorScheme)
  283. toggleTheme(chosenColorScheme)
  284. })
  285. const selectedTheme = localStorage.getItem('theme')
  286. if (selectedTheme && selectedTheme !== 'undefined') {
  287. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  288. }
  289. }
  290. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  291. window.addEventListener('load', () => {
  292. let hasDarkRules = false
  293. for (const styleSheet of Array.from(document.styleSheets)) {
  294. let mediaRules = []
  295. for (const cssRule of styleSheet.cssRules) {
  296. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  297. continue
  298. }
  299. // WARNING: Safari does not have/supports `conditionText`.
  300. if (cssRule.conditionText) {
  301. if (cssRule.conditionText !== prefersColorSchemeDark) {
  302. continue
  303. }
  304. } else {
  305. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  306. continue
  307. }
  308. }
  309. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  310. }
  311. // WARNING: do not try to insert a Rule to a styleSheet you are
  312. // currently iterating on, otherwise the browser will be stuck
  313. // in a infinite loop…
  314. for (const mediaRule of mediaRules) {
  315. styleSheet.insertRule(mediaRule.cssText)
  316. hasDarkRules = true
  317. }
  318. }
  319. if (hasDarkRules) {
  320. loadThemeForm('#theme-selector')
  321. }
  322. })
  323. </script>
  324. </body>
  325. </html>