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 85KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  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` element
  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="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <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>
  31. <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>
  32. <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>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://francoisbest.com/posts/2021/hashvatars">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>Representing SHA-256 Hashes As Avatars</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://francoisbest.com/posts/2021/hashvatars" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <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
  71. 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>
  72. <p class="chakra-text css-8fkoyy">They are little challenges I give myself, usually without too many stakes
  73. involved, and with small enough a scope so that I can ship it in a day or two,
  74. while keeping spare family time.</p>
  75. <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>
  76. experiment. Something like a blockchain explorer, but without the crypto overhead.</p>
  77. <p class="chakra-text css-8fkoyy">Each node in the system is represented by a hash. Because humans are
  78. terrible at reading and quickly identifying large numbers (other than by their
  79. first or last few digits), a visual representation is needed.</p>
  80. <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>
  81. 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>
  82. <p class="chakra-text css-8fkoyy">I wanted one that still looks abstract (not as opinionated as monsters or robots),
  83. and that plays nice in a rounded avatar UI component.</p>
  84. <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>
  85. <p class="chakra-text css-8fkoyy">If you are allergic to maths and trigonometry, feel free to play with the more
  86. detailed <a class="chakra-link css-y3jojq" href="/hashvatar">interactive example here</a>. Otherwise, let's dive into
  87. how it's done.</p>
  88. <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>
  89. <p class="chakra-text css-8fkoyy">Many of the existing solutions produce square images, yet avatars are often
  90. displayed as circles. They would lose information on the corners when rounded,
  91. so instead of a cartesian (x-y) approach, we're going to use polar (angle-radius)
  92. coordinates instead.</p>
  93. <p class="chakra-text css-8fkoyy">A grid in cartesian space maps to concentric circles and pie-like cuts in
  94. polar space.</p>
  95. <p class="chakra-text css-8fkoyy">SHA-256 hashes have 256 bits of information that we need to represent. Dividing
  96. a circle into 256 sections would make each section too small to be visually useful,
  97. and would only leave 1 bit of "value" to represent in each section
  98. (0 or 1, black or white).</p>
  99. <p class="chakra-text css-8fkoyy">Instead, we're going to divide the circle into 32 sections:</p>
  100. <ul role="list" class="css-80rt2u"><li class="css-77vupy">4 concentric rings</li><li class="css-77vupy">8 pie-like cuts</li></ul>
  101. <p class="chakra-text css-8fkoyy">The resulting SVG code for such a grid looks like this (in a React component):</p>
  102. <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>
  103. <span class="token keyword">const</span> r1 <span class="token operator">=</span> <span class="token number">1</span>
  104. <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>
  105. <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>
  106. <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>
  107. <span class="token keyword">return</span> <span class="token punctuation">(</span>
  108. <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">
  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>r1<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>r2<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>r3<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>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">
  113. </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">
  114. </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">
  115. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>line</span>
  116. <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>
  117. <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>
  118. <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>
  119. <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>
  120. <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
  121. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>line</span>
  122. <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>
  123. <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>
  124. <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>
  125. <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>
  126. <span class="token punctuation">/&gt;</span></span><span class="token plain-text">
  127. </span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">&gt;</span></span>
  128. <span class="token punctuation">)</span>
  129. <span class="token punctuation">}</span>
  130. </code></pre>
  131. <p class="chakra-text css-8fkoyy">Doing this naively, with each concentric ring's radius being 1/4th of the
  132. outermost/largest one gives us this:</p>
  133. <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>
  134. <p class="chakra-text css-8fkoyy">There are some issues: the innermost ring sections are tiny compared to the outermost.</p></p>
  135. <p class="chakra-text css-8fkoyy">If we calculate the radii so that each section has an equal area, we get
  136. the following result:</p>
  137. <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></p>
  138. <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>
  139. <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>
  140. <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
  141. <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>
  142. <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>
  143. <p class="chakra-text css-8fkoyy">Now that we have 32 nice looking sections on our circle, we can map each section
  144. to an 8-bit value in the hash.</p>
  145. <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>
  146. <pre><code>315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3
  147. </code></pre>
  148. <p class="chakra-text css-8fkoyy">We can split it in 32 blocks of 8 bits (2 hexadecimal digits), and organise them by
  149. 4 blocks of 8 to map to the rings:</p>
  150. <pre><code>12 o'clock -&gt; clockwise
  151. 31 5f 5b db 76 d0 78 c4 Outer ring
  152. 3b 8a c0 06 4e 4a 01 64 Middle-outer ring
  153. 61 2b 1f ce 77 c8 69 34 Middle-inner ring
  154. 5b fc 94 c7 58 94 ed d3 Inner ring
  155. </code></pre>
  156. <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.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>
  157. <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.
  158. Each section resembles a pie/pizza slice, going from the center
  159. of the circle to a given radius, and covering 1/8th of the circle.</p></p>
  160. <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>
  161. <p class="chakra-text css-8fkoyy">Since SVG uses cartesian coordinates, we'll have to convert our polar logic
  162. into cartesian SVG path commands:</p>
  163. <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>
  164. x<span class="token punctuation">:</span> <span class="token builtin">number</span>
  165. y<span class="token punctuation">:</span> <span class="token builtin">number</span>
  166. <span class="token punctuation">}</span>
  167. <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>
  168. <span class="token keyword">return</span> <span class="token punctuation">{</span>
  169. x<span class="token punctuation">:</span> radius <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">cos</span><span class="token punctuation">(</span><span class="token number">2</span> <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> 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>
  170. y<span class="token punctuation">:</span> radius <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">sin</span><span class="token punctuation">(</span><span class="token number">2</span> <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> 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>
  171. <span class="token punctuation">}</span>
  172. <span class="token punctuation">}</span>
  173. <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>
  174. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</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">`</span></span>
  175. <span class="token punctuation">}</span>
  176. <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>
  177. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</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">`</span></span>
  178. <span class="token punctuation">}</span>
  179. <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>
  180. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</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">`</span></span>
  181. <span class="token punctuation">}</span>
  182. <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>
  183. <span class="token keyword">const</span> angleA <span class="token operator">=</span> index <span class="token operator">/</span> <span class="token number">8</span>
  184. <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>
  185. <p class="mdx-marker"> <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token punctuation">[</span>
  186. </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>
  187. </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>
  188. </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>
  189. </p><p class="mdx-marker"> <span class="token string">'Z'</span>
  190. </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>
  191. </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>
  192. <span class="token punctuation">}</span>
  193. </code></pre>
  194. <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>
  195. <p class="chakra-text css-8fkoyy">Now we can turn each section's byte value into a colour.</p>
  196. <p class="chakra-text css-8fkoyy">For that, we have many options. 8 bits could map directly to 256 colours like the
  197. old Windows systems, but that would require a lookup table. Instead, we can
  198. 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>
  199. <p class="chakra-text css-8fkoyy">Hue is the component that has the most visual impact, while Saturation and
  200. Lightness can be used to add little variants to each section.</p>
  201. <p class="chakra-text css-8fkoyy">To map our 8 bit value to 3 components, we can divide the byte into:</p>
  202. <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>
  203. <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>
  204. <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>
  205. <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>
  206. <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>
  207. <span class="token keyword">const</span> normalizedH <span class="token operator">=</span> colorH <span class="token operator">/</span> <span class="token number">16</span>
  208. <span class="token keyword">const</span> normalizedS <span class="token operator">=</span> colorS <span class="token operator">/</span> <span class="token number">4</span>
  209. <span class="token keyword">const</span> normalizedL <span class="token operator">=</span> colorL <span class="token operator">/</span> <span class="token number">4</span>
  210. <span class="token keyword">const</span> h <span class="token operator">=</span> <span class="token number">360</span> <span class="token operator">*</span> normalizedH
  211. <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">*</span> normalizedS
  212. <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
  213. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</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">`</span></span>
  214. <span class="token punctuation">}</span>
  215. </code></pre>
  216. <p class="chakra-text css-8fkoyy">We can adjust the range for each component to get nice results:</p>
  217. <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></p>
  218. <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>
  219. <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>
  220. <p class="chakra-text css-8fkoyy">Our colour encoding suffers from a flaw: two hashes can look very similar, but
  221. have a few bits of difference here and there. They can go unnoticed especially
  222. if differences occur in the LSBs of hue/saturation/lightness components.</p>
  223. <p class="chakra-text css-8fkoyy">Also, the sections look random in colour, and the whole avatar lacks coherence.</p>
  224. <p class="chakra-text css-8fkoyy">It would be nice if there was some pattern to a hash that makes it
  225. random enough to be distinguished yet coherent enough within itself.
  226. A balance between order and chaos.</p>
  227. <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>
  228. <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>
  229. <pre class="language-ts"><code class="language-ts">
  230. <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>
  231. <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>
  232. <span class="token keyword">const</span> rings <span class="token operator">=</span> <span class="token punctuation">[</span>
  233. 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>
  234. 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">*</span> ringLength<span class="token punctuation">,</span> <span class="token number">2</span> <span class="token operator">*</span> ringLength<span class="token punctuation">)</span><span class="token punctuation">,</span>
  235. 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">*</span> ringLength<span class="token punctuation">,</span> <span class="token number">3</span> <span class="token operator">*</span> ringLength<span class="token punctuation">)</span><span class="token punctuation">,</span>
  236. 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">*</span> ringLength<span class="token punctuation">,</span> <span class="token number">4</span> <span class="token operator">*</span> ringLength<span class="token punctuation">)</span>
  237. <span class="token punctuation">]</span>
  238. <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>
  239. <span class="token keyword">return</span> <span class="token punctuation">{</span>
  240. 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">*</span> <span class="token number">2</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span>
  241. 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>
  242. <span class="token punctuation">}</span>
  243. <span class="token punctuation">}</span>
  244. </code></pre>
  245. <p class="chakra-text css-8fkoyy">These values give us additional parameters to play with in the colour calculation.</p>
  246. <p class="chakra-text css-8fkoyy">Notably, we can <em>"seed"</em> the Hue with the hash soul, and introduce hue varitions
  247. per-ring with each ring soul, and with the value itself.</p>
  248. <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>
  249. <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>
  250. <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>
  251. <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>
  252. <span class="token keyword">const</span> normalizedH <span class="token operator">=</span> colorH <span class="token operator">/</span> <span class="token number">16</span>
  253. <span class="token keyword">const</span> normalizedS <span class="token operator">=</span> colorS <span class="token operator">/</span> <span class="token number">4</span>
  254. <span class="token keyword">const</span> normalizedL <span class="token operator">=</span> colorL <span class="token operator">/</span> <span class="token number">4</span>
  255. <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">*</span> hashSoul
  256. </p><p class="mdx-marker"> <span class="token operator">+</span> <span class="token number">120</span> <span class="token operator">*</span> ringSoul
  257. </p><p class="mdx-marker"> <span class="token operator">+</span> <span class="token number">30</span> <span class="token operator">*</span> normalizedH
  258. </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">*</span> normalizedS
  259. <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
  260. <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</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">`</span></span>
  261. <span class="token punctuation">}</span>
  262. </code></pre>
  263. <p class="chakra-text css-8fkoyy">We can also introduce structural variations by changing each ring's starting
  264. angle based on the ring soul, to create a staggering effect:</p>
  265. <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>
  266. <p class="chakra-text css-8fkoyy">Not only does this help give a bit more uniqueness to the avatar, it also helps
  267. with accessibility for colour-blind people</p>
  268. <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>
  269. <p class="chakra-text css-8fkoyy">If we change the radius and flags for the arc part of the section paths and
  270. play with each ring's starting angle, we can obtain interesting variations:</p>
  271. <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>
  272. <p class="chakra-text css-8fkoyy">With a bit of tweaking in the colour mapping value, we can easily extend this
  273. technique to arbitrary hash lengths (as long as said length is divisible by 32).</p>
  274. <p class="chakra-text css-8fkoyy">It so happens that when I started this project, Centralized Coin was using SHA-256,
  275. but later on switched to SHA-384, which gives 12 bits per section.</p>
  276. <h2 class="chakra-heading css-1rjvsw4" id="conclusion">Conclusion<a class="chakra-link css-jchjfc" aria-label="anchor" href="#conclusion">#</a></h2>
  277. <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>
  278. in action in the
  279. <a target="_blank" rel="noopener noreferrer" class="chakra-link css-y3jojq" href="https://centralized-coin-explorer.vercel.app">Centralized Coin Explorer</a>,
  280. or play with the variants yourself <a class="chakra-link css-y3jojq" href="/hashvatar">on the playground</a>.</p>
  281. <p class="chakra-text css-8fkoyy">I will publish the code as an NPM package later, in the mean time the source
  282. <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>
  283. is on GitHub, as well as the
  284. <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>
  285. <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>
  286. </article>
  287. <hr>
  288. <footer>
  289. <p>
  290. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  291. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
  292. </svg> Accueil</a> •
  293. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  294. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-rss2"></use>
  295. </svg> Suivre</a> •
  296. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  297. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-user-tie"></use>
  298. </svg> Pro</a> •
  299. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  300. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-mail"></use>
  301. </svg> Email</a> •
  302. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  303. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-hammer2"></use>
  304. </svg> Légal</abbr>
  305. </p>
  306. <template id="theme-selector">
  307. <form>
  308. <fieldset>
  309. <legend><svg class="icon icon-brightness-contrast">
  310. <use xlink:href="/static/david/icons2/symbol-defs.svg#icon-brightness-contrast"></use>
  311. </svg> Thème</legend>
  312. <label>
  313. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  314. </label>
  315. <label>
  316. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  317. </label>
  318. <label>
  319. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  320. </label>
  321. </fieldset>
  322. </form>
  323. </template>
  324. </footer>
  325. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  326. <script>
  327. function loadThemeForm(templateName) {
  328. const themeSelectorTemplate = document.querySelector(templateName)
  329. const form = themeSelectorTemplate.content.firstElementChild
  330. themeSelectorTemplate.replaceWith(form)
  331. form.addEventListener('change', (e) => {
  332. const chosenColorScheme = e.target.value
  333. localStorage.setItem('theme', chosenColorScheme)
  334. toggleTheme(chosenColorScheme)
  335. })
  336. const selectedTheme = localStorage.getItem('theme')
  337. if (selectedTheme && selectedTheme !== 'undefined') {
  338. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  339. }
  340. }
  341. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  342. window.addEventListener('load', () => {
  343. let hasDarkRules = false
  344. for (const styleSheet of Array.from(document.styleSheets)) {
  345. let mediaRules = []
  346. for (const cssRule of styleSheet.cssRules) {
  347. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  348. continue
  349. }
  350. // WARNING: Safari does not have/supports `conditionText`.
  351. if (cssRule.conditionText) {
  352. if (cssRule.conditionText !== prefersColorSchemeDark) {
  353. continue
  354. }
  355. } else {
  356. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  357. continue
  358. }
  359. }
  360. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  361. }
  362. // WARNING: do not try to insert a Rule to a styleSheet you are
  363. // currently iterating on, otherwise the browser will be stuck
  364. // in a infinite loop…
  365. for (const mediaRule of mediaRules) {
  366. styleSheet.insertRule(mediaRule.cssText)
  367. hasDarkRules = true
  368. }
  369. }
  370. if (hasDarkRules) {
  371. loadThemeForm('#theme-selector')
  372. }
  373. })
  374. </script>
  375. </body>
  376. </html>