Previous 23 Next

Saving and loading

We will use the localStorage api for this All the progress will be lost if the user closes the window. and we need to be able to load back the data. There are several way games can do this we could also save our state at a server and then the user could provide login credentials to get hers load game. local storage is an api to access the web browsers storage and works by storing key value pairs. We save top score, unlocked characters and unlocked guns.

export class Persistence {

  private readonly allowStorageKey = "allowStorage"
  allowStorage = false

  private readonly scoreKey = "score"
  private score: number | null = null

  constructor() {
    this.allowStorage = window.localStorage.getItem(this.allowStorageKey) != null

  getScore() {
    if (!this.allowStorage) return 0
    if (this.score == null) {
      const score = window.localStorage.getItem(this.scoreKey)
      if (score == null) return 0
      this.score = parseInt(score)
    return this.score

  saveScore(score: number) {
    this.score = score
    if (!this.allowStorage) return
    window.localStorage.setItem(this.scoreKey, score.toString())

  userAllowedStorage() {
    this.allowStorage = true
    window.localStorage.setItem(this.allowStorageKey, "yes")
    if (this.score != null) this.saveScore(this.score)

User consent

The European Union has regulation concerning privacy, and we are in most cases required to get user concent before we can store any information in the browser.

export class CookieNotice {

    onAllowCookies: () => void
  ) {
    const divID = "CookieNotice"
    const buttonClass = "Button"
    const acceptID = "accept"
    const declineID = "decline"
    const textID = "text"

    const style = document.createElement("style")
    style.innerHTML = `
    #${divID} {
      font-family: sans-serif;
      position: absolute;
      bottom: 20px;
      width: 100%;
      max-width: 800px;
      background-color: #ffffff;
      box-shadow: 0 1px 2px 1px black;
      text-align: center;
      padding: 10px;
      box-sizing: border-box;
      left: 50%;
      transform: translateX(-50%);
      border-radius: 5px;

    #${textID} {
      font-size: 1.3rem;
      line-height: 2rem;

    #${divID} .${buttonClass} {
      box-shadow: 0 1px 2px -1px black;
      font-size: 2rem;
      color: white;
      text-shadow: 0 2px 2px black;
      border-style: none;
      border-radius: 5px;
      flex: 1;
      margin-left: 1rem;

    #${divID} ${buttonClass}:first-child {
      margin-right: 1rem;
      margin-left: 0;

    #${divID} ${buttonClass}:hover {
      cursor: pointer;

    #${acceptID} {
      background-color: #6abe30;

    #${declineID} {
      background-color: #ac3232;

    .row {
      padding-top: 20px;
      display: flex;


    const element = document.createRange().createContextualFragment(`
    <div id="${divID}">
      <div class="padding">
        <h1>Allow cookies?</h1>
        <div id="${textID}">
          Accepting cookies will store <i>allowedStorage</i> and <i>highscore</i>
          in your browser. <i>allowedStorage</i> will prevent this message from
          reappearing and <i>highscore</i> will store your highscore.
        <div class="row">
          <button id="${acceptID}" class="${buttonClass}">Yes</button>
          <button id="${declineID}" class="${buttonClass}">No</button>

    const div = document.getElementById(divID)
    const acceptButton = document.getElementById(acceptID)
    const declineButton = document.getElementById(declineID)

    if (div && acceptButton && declineButton) {
      acceptButton.addEventListener("click", () => {

      declineButton.addEventListener("click", () => {


Previous 23 Next