Welcome! :-)

This is both a wiki (a website editable by all) and a blog (an online diary about the stuff Alex Schroeder reads and does). If you’re a friend or relative, you might be interested in reading Life instead of this page. If you’ve come here from an RPG blog, you might want to head over to RPG. There are other similar categories to be found on the SiteMap.

Für Rollenspieler gibt es ebenfalls eine eigene RSP Kategorie.

2018-05-19 Security Focused Operating System


Most security folks know about Kali Linux, but there are quite a few alternative security-focused Linux distributions out there.

Parrot is a GNU/Linux distribution based on Debian and focused on Penetration Testing, Digital Forensics, Programming and Privacy protection.

You can download different spins (including an ARM version that can run on the RPi) and try it out for yourself.


Add Comment

2018-05-17 Writing Resources

Some links:

  • MHRA Style Guide Online, by the Modern Humanities Research Association (MHRA)
  • Oxford English Dictionary, Oxford Style Manual (both in paper)


Add Comment

2018-05-17 Hex Grids

There’s a beautiful article by Herman Tulleken on Gamasutra, 20 Fun Grid Facts (Hex Grids), from 2014.

The part about the coordinates reminds me of some of the code I have written...

One Step To

# Brute forcing the "next" step by trying all the neighbors. The
# connection data to connect to neighbouring hexes.
# Example Map             Index for the array
#      0201                      2
#  0102    0302               1     3
#      0202    0402
#  0103    0303               6     4
#      0203    0403              5
#  0104    0304
#  Note that the arithmetic changes when x is odd.

sub one_step_to {
  my ($self, $other) = @_;
  my $delta = [[[-1,  0], [ 0, -1], [+1,  0], [+1, +1], [ 0, +1], [-1, +1]],  # even
	       [[-1, -1], [ 0, -1], [+1, -1], [+1,  0], [ 0, +1], [-1,  0]]]; # odd
  my ($min, $best);
  for my $i (0 .. 5) {
    # make a new guess
    my ($x, $y) = ($self->x + $delta->[$self->x % 2]->[$i]->[0],
		   $self->y + $delta->[$self->x % 2]->[$i]->[1]);
    my $d = ($other->x - $x) * ($other->x - $x)
          + ($other->y - $y) * ($other->y - $y);
    if (!defined($min) || $d < $min) {
      $min = $d;
      $best = Point->new(x => $x, y => $y);
  return $best;


sub distance {
  my ($x1, $y1, $x2, $y2) = @_;
  if (@_ == 2) {
    ($x1, $y1, $x2, $y2) = map { xy($_) } @_;
  # transform the coordinate system into a decent system with one axis tilted by
  # 60°
  $y1 = $y1 - POSIX::ceil($x1/2);
  $y2 = $y2 - POSIX::ceil($x2/2);
  if ($x1 > $x2) {
    # only consider moves from left to right and transpose start and
    # end point to make it so
    my ($t1, $t2) = ($x1, $y1);
    ($x1, $y1) = ($x2, $y2);
    ($x2, $y2) = ($t1, $t2);
  if ($y2>=$y1) {
    # if it the move has a downwards component add Δx and Δy
    return $x2-$x1 + $y2-$y1;
  } else {
    # else just take the larger of Δx and Δy
    return $x2-$x1 > $y1-$y2 ? $x2-$x1 : $y1-$y2;


Add Comment

2018-05-16 Podcasts

Podcasts im currently listening to on a regular basis:

  • Thinking Allowed
  • In Our Time
  • 99% Invisible
  • Dan Carlin’s Hardcore History
  • History of the Crusades
  • The Allusionist
  • The British History Podcast
  • The Chinese History Podcast
  • The History of Byzantium
  • What Trump Can Teach Us About Con Law

Actually I’ve had to change the settings in my pod catcher: it no longer downloads episodes unless I explicitly ask it to. There is just so much stuff to listen to.

If you compare this list to earlier lists you will have noticed some changes. I haven’t listened to any episodes of these podcasts, recently:

  • History of Indian and Africana Philosophy
  • History of Philosophy Without Any Gaps
  • Planet Money
  • Revolutions
  • The History of WWII Podcast
  • This American Life

I haven’t unsubscribed, so perhaps I’ll get back to them during the summer holidays.

And I have some podcasts on my list which I have yet to check out:

  • Romance of the Three Kingdoms Podcast
  • Showcase from Radiotopia (where I really liked the first season about digital sound)


Comments on 2018-05-16 Podcasts

I started reading Romance of the Three Kingdoms a while ago but stopped after getting about halfway. I guess I do not have enough context to appreciate it. Like the Mahabharata, I guess. The podcast seems like a nicer introduction.

You might want to checkout,

  • Hidden Brain
  • More Perfect (Season 1 was particularly good)
  • Philosophise This (and Existential Comics)
  • HistoriaCivilis (on YouTube)

While listening to podcasts is great during my commute or travelling, the ad supported business model is not compatible with news (or opinion, I can’t tell the difference). I stopped consuming any type of news media, newspapers, TV, podcasts; about 5 years ago and to my surprise my life is no different.

AlokSingh 2018-05-18 09:14 UTC

I started reading Romance of the Three Kingdoms a few years ago and got even less far. I think it was just a few pages in when I stopped so the podcast is my attempt at turning it into an audio book plus commentary which I might appreciate on my commute.

As for news, I totally concur. I have trouble reading my weekly and my monthly newspaper (Wochenzeitung, a left leaning weekly paper, and the German edition of Le Monde Diplomatique). And the daily news just flows by as a stream of headlines in social media. I don’t often click through.

– Alex Schroeder 2018-05-18 10:41 UTC

Add Comment

2018-05-15 Sitelen Mute

I occasionally work on Sitelen Mute, my fork of fgallery. It is a static photo gallery generator with no frills that has a stylish, minimalist look. It shows your photos, and nothing else. There is no server-side processing, only static generation. The resulting gallery can be uploaded anywhere without additional requirements. It even works without Javascript. :)



Add Comment

2018-05-15 Game Development

Game Making Wiki, by @ryliejamesthomas. This sounds great.

A while back I mentioned some minimal game development environments:

  • LIKO-12. LIKO-12 is an open source fantasy computer completely written in the Lua programming language where you can make, play and share tiny retro-looking games and programs.
  • TIC-80. TIC-80 is a fantasy computer for making, playing and sharing tiny games. Same thing.
  • PX8. Open source Fantasy Console in Rust (with Python and Lua).
  • PICOLOVE, an implementation of the PICO-8 API for LOVE.

All of these descendants of PICO-8, I guess?


Add Comment

2018-05-14 Advertising

I just read a blog post about the GDPR and ads that I would like to share. Somewhere in the middle there is this image.


And below it: «It was a survey of ad industry people: “300+ publishers, adtech, brands, and various others…” Pause for a moment and look at that chart again. Nearly all those proffesionals in the business would not accept what their businesses do to other human beings.»

Pause for a moment, yes indeed.

And yes, I try to use ad blockers at all times.


Comments on 2018-05-14 Advertising

From a toot on Mastodon I wrote: Changing jobs is hard and there is hardship all over the world. People working in adtech will suffer for sure and that makes me sad, yes. But they worked in adtech, tracking me, selling my data to the highest bidder, circumventing my blockers, inventing permanent cookies, exploiting the bugs in my browsers, they have been real and active enemies of my online life for a great many years and for this evil intent and their callousness I shudder in disgust.

– Alex Schroeder 2018-05-15 10:50 UTC

Some great messages in this thread [1][2], and a link to If digital advertising sneezes, will content marketing catch a cold?

– Alex Schroeder 2018-05-16 08:08 UTC

Add Comment

2018-05-13 Looking for Music

Do you know of any choral works for a melancholy heart like mine? Something like the German Requiem by Brahms or All-Night Vigil by Rachmaninov? Something to instil some spirituality in my hardened little atheist heart? I didn’t find Beethoven’s Missa Solemnis but it didn’t touch me. Should I give it another try? Something else?

On Mastodon people have suggested:

  • Max Richter’s Blue Notebooks (no choir, unfortunately)
  • Bach’s St Matthew Passion (but when I listened in on YouTube it felt a bit like a bible reading)
  • Pärt’s Nunc Dimittis ( definitely could use some more Arvo Pärt! Cantus in Memoriam Benjamin Britten, Miserez, and other pieces are already favorites of mine.)
  • Fauré’s Requiem
  • Byrd’s Emendemus in melius



Comments on 2018-05-13 Looking for Music

I’ll suggest the Ode an die Freude, but I’ll do that at any provocation or none, so take the suggestion for what it’s worth in such light. I will say, if you can listen to the entire piece uninterrupted and find yourself totally unmoved throughout, it might be worth checking to make sure you haven’t mislaid your pulse somewhere...

– Aaron 2018-05-14 01:36 UTC

Hah. I was asking for melancholy music. The problem is right there, in the name: Joy. But yes, it is a great piece of music. :)


Some of the names I was given led me to this wiki I had never heard of: ChoralWiki, home of the Choral Public Domain Library! “Here you will find free choral/vocal scores, texts, translations, and other useful information.” I love that these things exist.

– Alex Schroeder 2018-05-14 06:06 UTC

Add Comment

2018-05-12 Mastodon OAuth2 Issues

I signed up for a new instance and noticed that Mastodon Archive doesn’t allow me to log in using OAuth2. What’s going on? Let’s look at the API again.

Register the application:

curl -X POST -d "client_name=mastodon-archive&redirect_uris=urn:ietf:wg:oauth:2.0:oob&scopes=read" -Ss https://tabletop.social/api/v1/apps

This gives me a JSON containing client_id and client_secret. Store these as environment variables of the same name.

Store the password in an environment variable, too.

export client_id=...
export client_secret=...
export password=...

Login to get the access token:

curl -X POST -d "client_id=$client_id&client_secret=$client_secret&grant_type=password&username=kensanata@gmail.com&password=$password" -Ss https://tabletop.social/oauth/token

Store it in an environment variable, of course.

export access=token=...

Test it:

curl --header "Authorization: Bearer $access_token" -sS https://tabletop.social/api/v1/timelines/home

This seems to work even though it doesn’t work for Mastodon Archive!

Let’s create the necessary files, manually:

echo $client_id > tabletop.social.client.secret
echo $client_secret >> tabletop.social.client.secret
echo $access_token > tabletop.social.user.kensanata.secret

And test it:

$ mastodon-archive archive kensanata@tabletop.social
Get user info
Get statuses (this may take a while)
Get favourites (this may take a while)
Skipping mentions
Skipping followers
Saving 33 statuses, 13 favourites, 0 mentions, and 0 followers

Thus, we now know that the problem is the app registration and obtaining the access token. Everything else works as expected.


Comments on 2018-05-12 Mastodon OAuth2 Issues

Debugging what Mastodon.py does... Here is what my code does, essentially:

print("Registering app")
    api_base_url = url,
    to_file = client_secret)

print("Log in")
mastodon = Mastodon(
    client_id = client_secret,
    api_base_url = url,
    debug_requests = True)

url = mastodon.auth_request_url(
    client_id = client_secret,

print("Visit the following URL and authorize the app:")

print("Then paste the access token here:")
token = sys.stdin.readline().rstrip()

    username = username,
    code = token,
    to_file = user_secret,

And I get the error in the last statement.

Registering app
Log in
Mastodon: Request to endpoint "/api/v1/instance/" using method "GET".
Parameters: {}
Headers: None
Files: {}
Mastodon: Response received with code 200.
response headers: {...}
Response text content: {...}
Visit the following URL and authorize the app:
Then paste the access token here:
Mastodon: Request to endpoint "/oauth/token" using method "POST".
Parameters: {'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', 'code': '...033d', 'grant_type': 'authorization_code', 'client_id': '...8aba', 'client_secret': '...b138', 'scope': 'read'}
Headers: None
Files: {}
Mastodon: Response received with code 401.
response headers: {... 'WWW-Authenticate': 'Bearer realm="Doorkeeper", error="invalid_grant", error_description="The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."' ...}
Response text content: {"error":"invalid_grant","error_description":"The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."}

I started wondering: is this because of the scope parameter? I was missing the scope parameter in my call to create_app. I made that change but saw no difference.

I then thought perhaps the problem is the missing redirect_uris in auth_request_url and redirect_uri in log_in. I added them both but saw no difference.

I then thought perhaps something about scopes was wrong so I removed scopes, redirect_uris and redirect_uri from the various calls but saw no difference.

Sadly, the Mastodon.py documentation and the Doorkeeper documentation it refers to is very sparse indeed. Mastodon.py doesn’t tell me how to really do OAuth and Doorkeeper only tells me how to do it with a password (Resource Owner Password Credentials) and not how to use Client Credentials.

It is all quite frustrating.

And most disturbing of all: it continues to work for octodon.social but doesn’t work for any of the other instances I have tried, e.g. social.nasqueron.org.

As I said, it is all quite frustrating.

As expected, switching the entire thing to password credentials “works”:

mastodon = Mastodon(
    client_id = client_secret,
    api_base_url = url)

print("We need your credentials just this once to log in.")
sys.stdout.write("Email: ")
email = sys.stdin.readline().rstrip()
sys.stdout.write("Password: ")
password = sys.stdin.readline().rstrip()

    username = email,
    password = password,
    to_file = user_secret,

The result:

$ mastodon-archive login kensanata@social.nasqueron.org
We need your credentials just this once to log in.
Email: kensanata+nasqueron@gmail.com
Password: *secret*
Get user info
Login OK

The word of the day is “frustrating”.

– Alex Schroeder 2018-05-13 19:23 UTC

Add Comment

2018-05-11 Charming

How do you run charmed characters?

I find charms work best of combined with simple commands by the referee. I play it like Jedi mind tricks. “You will protect me with your life.” Or “You will fetch the black sword for me.” Combine this with a simple “you may roll a save if allies of the magic user attack you.” That also means it won’t work for long periods of time because reduced agency is obviously frustrating. But it’s still better than telling them to role play being friends with the bad guys. That only works if the players are into that kind of messing with their friends. Sometimes they are and then it’s hilarious.


Add Comment



Please make sure you contribute only your own work, or work licensed under the GNU Free Documentation License. Note: in order to facilitate peer review and fight vandalism, we will store your IP number for a number of days. See Privacy for more information. See Info for text formatting rules. You can edit the comment page if you need to fix typos. You can subscribe to new comments by email without leaving a comment.

To save this page you must answer this question:

Please say HELLO.

Referrers: Strange Magic: Bene Gesserit Class for Swords and Wizardry frothsof D&D Strange Magic Gothridge Manor Diary Dreams of Mythic Fantasy ZENOPUS ARCHIVES: David Sutherland Day 2018