Implementing a Konami Code Easter Egg in Svelte by Leveraging Svelte Stores
Tags: svelte, tutorial, webdev, typescript
Published: 2023-07-11 10:11:28

The [Konami Code](https://en.wikipedia.org/wiki/Konami_Code) is a originally a cheat code that now belongs to the pop culture.

It's a fairly traditional code that consists of the following key sequence: `↑` `↑` `↓` `↓` `←` `→` `←` `→` `B` `A`

Initially used in many Konami Games, it then spreads to other video games and [even in other software and websites](https://en.wikipedia.org/wiki/Konami_Code#Uses_outside_of_video_games) such as in one of the [Bank of Canada's website](https://www.bankofcanada.ca/banknotes/bank-note-series/commemorative-notes/banknote150/)!

Since it has spread so far into the "geek culture", why not incorporating it into your next website?

In today's article, we are going to see how to detect the Konami Code in Svelte by leveraging the concept of stores in order to create a small [easter egg](https://en.wikipedia.org/wiki/Easter_egg_(media)) 🥚.

## Table of Contents

- [Referencing the Konami Code](#referencing-the-konami-code)
- [Detecting the Last Pressed Keys](#detecting-the-last-pressed-keysc)
- [Detecting the Konami Code](#detecting-the-konami-code)
- [Reacting to Its Detection](#reacting-to-its-detection)

---

## Referencing the Konami Code

The Konami Code will be a sequence of keys that can either be:

- `Arrow Up`
- `Arrow Down`
- `Arrow Left`
- `Arrow Right`
- `a` in lowercase or uppercase
- `b` in lowercase or uppercase

From that point, we can create a custom type to work with:

```ts
type KeyCode =
  | 'ArrowUp'
  | 'ArrowDown'
  | 'ArrowLeft'
  | 'ArrowRight'
  | 'KeyB'
  | 'KeyA'
  | undefined;  // 👈 Other keys are to be ignored
```

We can then represent the Konami Code as a sequence of `KeyCode`s:

```ts
const konamiCode: KeyCode[] = [
  'ArrowUp',
  'ArrowUp',
  'ArrowDown',
  'ArrowDown',
  'ArrowLeft',
  'ArrowRight',
  'ArrowLeft',
  'ArrowRight',
  'KeyB',
  'KeyA',
];
```

## Detecting the Last Pressed Keys

### Creating a readable store

The last pressed keys sequence will be store that can only be modified when a key is pressed, not from the outside.

For that, Svelte has the notion of [readable store](https://svelte.dev/docs/svelte-store#readable).

In our case, we want the store to listen to any key down event when first subscribed and to remove this listener when unsubscribed:

```ts
const lastKeyPressed = readable(
  new Array<KeyCode>(),
  () => {
    const handleKeyDown = (event: KeyboardEvent): void => { };

    window.addEventListener('keydown', handleKeyDown);

    return () => window.removeEventListener('keydown', handleKeyDown);
  }
);
```

And just like that we created a store that will hold a sequence of `KeyCode` (or `undefined` if a pressed key does not belong to the Konami Code)!

### Implementing the `handleKeyDown` function

Now that we have our container, we still have to implement the logic that will be executed on each key pressed.

What we would ideally like is to map a pressed key to a `KeyCode` and append it to the sequence.

We can start by creating a map that will convert a the `KeyboardEvent.key` property to a `KeyCode`:

```ts
const keyMap: Record<string, KeyCode> = {
  ArrowUp: 'ArrowUp',
  ArrowDown: 'ArrowDown',
  ArrowLeft: 'ArrowLeft',
  ArrowRight: 'ArrowRight',
  a: 'KeyA',
  b: 'KeyB',
  A: 'KeyA',
  B: 'KeyB',
};
```

What is left is to use it to detect which `KeyCode` has been pressed:

```ts
const lastKeyPressed = readable(
  new Array<KeyCode>(),
  () => {
    const handleKeyDown = (event: KeyboardEvent): void => {
      const keyPressed = keyMap[event.key];
    };

    window.addEventListener('keydown', handleKeyDown);

    return () => window.removeEventListener('keydown', handleKeyDown);
  }
);
```

However, we might have the key but we do not have any way to access the current value held by the store.

For that, the function used in `readable` takes two additional parameters:

- A `set` function that can be used to set the value of the store
- An `update` function that uses a callback to modify the current value of the store

While `set` would not help in *appending* a value, `update` definitely will, let's grab it:

```ts
const lastKeyPressed = readable(
  new Array<KeyCode>(),
  (_set, update) => {
    const handleKeyDown = (event: KeyboardEvent): void => {
      const keyPressed = keyMap[event.key];

      update((currentSequence: KeyCode[]) => {
        const next = currentSequence.concat(keyPressed);

        // 👇 Discards the oldest value if the sequence is too long
        if (next.length > konamiCode.length) {
          next.splice(0, 1);
        }

        return next;
      });
    };

    window.addEventListener('keydown', handleKeyDown);

    return () => window.removeEventListener('keydown', handleKeyDown);
  }
);
```

We now have a store that is storing the latest pressed keys and exposes them as sequence of the Konami Code.

Now onward to detecting it!

## Detecting the Konami Code

We have several ways of signaling to a consumer when the Konami Code has been entered.

However, as I'm thinking of stores as a kind of [RxJS's Observables](https://rxjs.dev/guide/observable), I tend to prefer deriving a value from it to keep my code as declarative as possible.

> If the concept of "declarative code" is not something you are familiar with, Joshua Morony has some wonderful videos on that subject: {% embed https://www.youtube.com/watch?v=skOTEbGwncE %}

Hopefully, it seems to also be the case for the Svelte core dev team as it is possible to derive a store from another one by using [`derived`](https://svelte.dev/docs/svelte-store#derived).

To derive a store, the syntax is pretty straightforward:

- As a first parameter, the `derived` function is expecting the store to derive its value from
- As a second parameter, we should define a function that takes the value emitted by the store we derived from and infer the value of the current store

In our case, the inferred value will simply be a boolean evaluated to `true` if the current sequence matches the Konami Code:

```ts
// 👇 Don't forget to export this store!
export const isKonamiCodePressed: Readable<boolean> = derived(
  lastKeyPressed,
  ($pressedKeys: KeyCode[]) => JSON.stringify($pressedKeys) === JSON.stringify(konamiCode)
);
```

> We could have computed the value of `JSON.stringify(konamiCode)` instead of recomputing it everytime but for the sake of this tutorial I will spare you some additional code

## Reacting to Its Detection

With our Konami Code detection store, we can now use it in our Svelte app to add a small easter egg.

In a new Svelte component, all we have to do is to subscribe to the store and react to whenever it has been entered:

```html
<script lang="ts">
  import { onDestroy, onMount } from 'svelte';
  import type { Unsubscriber } from 'svelte/store';

  import { isKonamiCodePressed } from '$lib/stores/konami';
  

  let unsubscribe: Unsubscriber = () => {};

  onMount(() => {
    unsubscribe = isKonamiCodePressed.subscribe(
      (isKonamiCodePressed: boolean): void => {
        if (!isKonamiCodePressed) return;
        console.log('Konami Code detected!');
      }
    );
  });

  // 👇 Don't forget to remove the event listener
  onDestroy(unsubscribe);
</script>

```

While it works, a `console.log` might not be the most funny thing we can do with that.

Let's build something visual and unexpected, like confetti 🎊!

Grab [JS Confetti](https://github.com/loonywizard/js-confetti):

```sh
npm i js-confetti
```

And replace the former code with the following one:

```html
<script lang="ts">
  import { onDestroy, onMount } from 'svelte';
  import type { Unsubscriber } from 'svelte/store';

  import JSConfetti from 'js-confetti';

  import { isKonamiCodePressed } from '$lib/stores/konami';

  let unsubscribe: Unsubscriber = () => {};

  onMount(() => {
    const jsConfetti = new JSConfetti();

    unsubscribe = isKonamiCodePressed.subscribe(
      (isKonamiCodePressed: boolean): void => {
        if (!isKonamiCodePressed) return;

        jsConfetti.addConfetti();
      }
    );
  });

  onDestroy(unsubscribe);
</script>
```

You can now use this component on whatever page you want the user to be able to enter the Konami Code !

> Wanna see a live example ? Try it out on [my landing page](https://pbouillon.github.io)!

> You can also see the implementation from which this article is inspired from the sources:
> {% embed https://github.com/pBouillon/pbouillon.github.io %}

---

I hope that you learn something useful there!

{% embed https://dev.to/pbouillon %}

---

> Photo by <a href="https://unsplash.com/@anniespratt?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Annie Spratt</a> on <a href="https://unsplash.com/photos/43o1KVbOWXA?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>