Introducing Harmonizers
Harmonizers let you use your own HTML to octothorpe
Last modified 2025-08-03
From the start we wanted Octothorpes to be as easy to use as possible. The original way to put them on your site only takes two lines of simple HTML. But even that can be a challenge with some website builders or hosting platforms. So we built harmonizers so you can use Octothorpe Protocol (OP) without changing the markup on your site.
An OP Harmonizer is a small text file that tells an OP server what to look for on your webpage. If you already have tags on your blog, for example, you can use a harmonizer to say "use my tags as octothorpes." Then, when the server looks at your page, it will interpret your tag as an octothorpe, magically turning your tags into a hashtag, connected to other sites using the same tag. All without you having to change the HTML on your page.
Right now, you still have to include one line of HTML in the head of your webpage to ask an OP server to look at your page. OP Servers never look at pages unless you ask them to. We're working on products that will let you use OP in other ways, but for now you still need to be able to edit the head of your page.
The technical part
At the moment, you will still need a little bit of knowledge of HTML to make your own harmonizer. If you're comfortable with that, read on.
Here's how a harmonizer works:
Harmonizers are just JSON files that define what selectors to use to look for Octothorpe statements on your page.
The default harmonizer looks for link
elements with a rel
attribute of octo:octothorpe
So if you put <link href='harmonizers-are-cool' rel='octo:octothorpe'>
will get picked up and turned into a hashtag.
Since "tag" and "hashtag" and "octothorpe" can get a little confusing, we will use the technical term for them in OP, which is
Term
. A term is defined in the database as "octo:Term", and is defined by a public urls on an OP server that follows this pattern:server-name/~/term
. An octothorpe to a url like that is an octothrpe to aterm
, whereas an octothorpe to any other kind of url is an octothorpe to apage
. Octothorping a term is what we call a "hashtag" or a "tag" or just an "octothorpe" for short. Octothorping apage
is alink
, which can have many subtypes, such asbookmark
orbacklink
.
The rule in the harmonizer looks like this:
{
"selector": "link[rel='octo:octothorpes']",
"attribute": "href"
}
That's easy enough, but thanks to harmonizers we don't have to change anything on the page to use octothorpes.
Let's say your site already has tags, and they show up as keywords in the head
of your pages.
So your page tagged harmonizers-are-cool
has <meta name="keywords" content="harmonizers-are-cool">
in the head. That's easy for harmonizers to find. So easy in fact we already have a harmonizer for it. Here's how to use it:
<link rel="preload" as="fetch" href="OP-SERVER/?uri=PAGE&as=keywords">
That will index your page using the harmonizer named keywords
. Any rules present there will override the same rules in the default harmonizer. The rule it has for octothorpes looks like this:
{
"selector": "meta[name='keywords']",
"attribute": "content",
"postProcess": {
"method": "split",
"params": `,`
}
but wait, what if you have multiple keywords? <meta name="keywords" content="harmonizers-are-cool, cats, other-stuff">
No problem. Harmonizer spec lets you define some basic filtering and post-processing of the matched content. That's the postProcess
part of the rule.
To see how all these harmonizers work, let's head over to the orchestra pit. That's what we call the testing / debugging endpoint we built for harmonizers.
If you send your url to the orchestra pit, it will return both the data it's picked up form your url, and the ruleset for the harmonizer used.
Here's what this page looks like using the default harmonizer. If we hit https://octothorp.es/debug/orchestra-pit?uri=https://demo.ideastore.dev
{
"@id": "https://demo.ideastore.dev",
"title": "Home - Octothorpes Demo",
"description": "Welcome to the demonstration site for the Octothorpes protocol. Here you can see standard octothorpes and [backlinks](/backlinked-page) in action, plus some suggestions for [shorthand](/shorthand) methods of making them.",
"image": "https://demo.ideastore.dev/assets/octomark.png",
"contact": "",
"type": "",
"octothorpes": [
"octothorpes",
"demo",
{
"type": "link",
"uri": "https://ideastore.dev/blog/why-tho"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/backlinked-page"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/tags-and-octothorpes"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/shorthand"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/synonyms"
},
{
"type": "endorse",
"uri": "https://octothorpenty.glitch.me"
}
],
"harmonizerUsed": {
"@context": "https://octothorp.es/context.json",
"@id": "https://octothorp.es/harmonizer/default",
"@type": "harmonizer",
"title": "Default Octothorpe Harmonizer",
"mode": "html",
"schema": {
"subject": {
"s": "source",
"title": [
{
"selector": "title",
"attribute": "textContent"
}
],
"description": [
{
"selector": "meta[name='description']",
"attribute": "content"
}
],
"image": [
{
"selector": "meta[property='og:image']",
"attribute": "content"
},
{
"selector": "link[rel='octo:image']",
"attribute": "href"
},
{
"selector": "[data-octo-image]",
"attribute": "href"
},
{
"selector": "[data-octo-image]",
"attribute": "src"
}
],
"contact": [
{
"selector": "meta[property='octo:contact']",
"attribute": "content"
}
],
"type": [
{
"selector": "meta[property='octo:type']",
"attribute": "content"
}
]
},
"hashtag": {
"s": "source",
"o": [
{
"selector": "octo-thorpe",
"attribute": "textContent"
},
{
"selector": "a[rel='octo:octothorpes']",
"attribute": "href",
"postProcess": {
"method": "regex",
"params": "https://octothorp.es/~/([^/]+)"
}
},
{
"selector": "link[rel='octo:octothorpes']",
"attribute": "href"
}
]
},
"link": {
"s": "source",
"o": [
{
"selector": "a[rel='octo:octothorpes']:not([href*='https://octothorp.es/~/'])",
"attribute": "href"
}
]
},
"endorse": {
"s": "source",
"o": [
{
"selector": "[rel='octo:endorses']:not([href*='https://octothorp.es/~/'])",
"attribute": "href"
}
]
},
"bookmark": {
"s": "source",
"o": [
{
"selector": "[rel='octo:bookmarks']:not([href*='https://octothorp.es/~/'])",
"attribute": "href"
}
]
},
"cite": {
"s": "source",
"o": [
{
"selector": "[rel='octo:cites']:not([href*='https://octothorp.es/~/'])",
"attribute": "href"
}
]
}
}
}
}
And here's what it looks like using the keywords
harmonizer.
https://octothorp.es/debug/orchestra-pit?uri=https://demo.ideastore.dev&as=keywords
{
"@id": "https://demo.ideastore.dev",
"title": "Home - Octothorpes Demo",
"description": "Welcome to the demonstration site for the Octothorpes protocol. Here you can see standard octothorpes and [backlinks](/backlinked-page) in action, plus some suggestions for [shorthand](/shorthand) methods of making them.",
"image": "https://demo.ideastore.dev/assets/octomark.png",
"contact": "",
"type": "",
"octothorpes": [
{
"type": "link",
"uri": "https://ideastore.dev/blog/why-tho"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/backlinked-page"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/tags-and-octothorpes"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/shorthand"
},
{
"type": "link",
"uri": "https://demo.ideastore.dev/synonyms"
},
{
"type": "endorse",
"uri": "https://octothorpenty.glitch.me"
}
],
"harmonizerUsed": {
"@context": "https://octothorp.es/context.json",
"@id": "https://octothorp.es/harmonizer/keywords",
"@type": "harmonizer",
"title": "Meta Kewords to Octothorpes Harmonizer",
"mode": "html",
"schema": {
"hashtag": {
"s": "source",
"o": [
{
"selector": "meta[name='keywords']",
"attribute": "content",
"postProcess": {
"method": "split",
"params": ","
}
}
]
}
}
}
}
As you can see, we can specify different harmonizers using the as
parameter. You can use a harmonizer id as shorthand if it exists on the server, otherwise you have to provide a valid url to a well-formed harmonizer definition.
Custom cases
Let's say that your blog uses the class look-a-tag
on the standard a
element. Why? Who knows? We don't judge, we just harmonize. You could easily write a harmonizer to find that.
TKTK more examples to come