Paste HTML and get the 'sha256-…' token for every inline <script>, <style> and event handler, computed the way a browser computes it: over the exact text between the tags, newline and indentation included, untrimmed. The hashing happens in this tab with crypto.subtle.digest; the HTML you paste goes nowhere.
Ctrl+Enter also runs it. Highlighted characters in the results are whitespace that is part of the hash.
For an inline <script> or <style>, CSP hashes the element's child text content: every character between the > that closes the start tag and the </script> that follows. The newline after the start tag and the indentation before the closing tag are part of it. Nothing is trimmed and no entities are decoded, because script and style are raw text elements. The parser does one thing first: CR LF and lone CR become LF (the "newline normalization" step of the input stream), so a file with Windows line endings is hashed as if it had Unix ones. The text is encoded as UTF-8, digested, base64-encoded, and written as 'sha256-<base64>'; sha384 and sha512 work the same way.
The obvious approach is to parse the page, walk the elements and hash el.innerHTML or the output of outerHTML. Serialising the DOM gives you the parser's version of the document, not the file: attribute values come back re-escaped (", &), tag names lower-cased, boolean attributes normalised, and any element the parser moved (a <script> written before <head>, say) sits somewhere else. DevTools makes it worse: "Edit as HTML" and the pretty-printed Sources panel re-indent, and copying from there changes the bytes. Then there is whatever the server does between your editor and the browser: a minifier that strips the leading newline, a template engine that injects whitespace, a CMS that reformats. One byte off and the hash silently stops matching.
So this tool uses DOMParser only to find out which elements exist and in what order, then locates each one's text in the original source string (the parsed textContent matched back to the source, checked to sit right after the start tag) and hashes that slice. If the DOM text and the source slice ever differ, the result says so. Attribute values are the exception: the browser hashes the decoded value, which is what getAttribute returns, so onclick="alert("hi")" is hashed as alert("hi").
A hash in script-src only covers <script> elements. Inline handlers (onclick, onload, …) and javascript: URLs stay blocked unless the directive also carries 'unsafe-hashes', in which case a hash of the attribute value allows that handler. The same goes for style="…" attributes under style-src. CSP3 browsers also accept script-src-attr and style-src-attr for these, which keeps the element hashes and the attribute hashes in separate lists. Rewriting the handler as an addEventListener call inside a hashed script is still the cleaner fix.
'self' covers same-origin src files; the tool adds the origin of any cross-origin <script src> or stylesheet link it sees. Once a hash or nonce is present in a directive, browsers ignore 'unsafe-inline' there, so the two lines are meant to replace it, not sit next to it. Data blocks such as <script type="application/json"> are never executed and do not need a hash. Hashes suit HTML that does not change per request; if the inline script is generated on the fly, a per-response nonce is the better tool.