A place to cache linked articles (think custom and personal wayback machine)
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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>