<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ブラウザゲーム &#8211; Tranquilvæj</title>
	<atom:link href="https://tranquilguides.com/tag/%e3%83%96%e3%83%a9%e3%82%a6%e3%82%b6%e3%82%b2%e3%83%bc%e3%83%a0/feed/" rel="self" type="application/rss+xml" />
	<link>https://tranquilguides.com</link>
	<description>記憶と身体、時間と不在をめぐる、文字と体験</description>
	<lastBuildDate>Fri, 04 Sep 2026 09:35:46 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://i0.wp.com/tranquilguides.com/wp-content/uploads/2026/01/cropped-IMG_3364.jpeg?fit=32%2C32&#038;ssl=1</url>
	<title>ブラウザゲーム &#8211; Tranquilvæj</title>
	<link>https://tranquilguides.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">251661265</site>	<item>
		<title>あける</title>
		<link>https://tranquilguides.com/akeru/</link>
					<comments>https://tranquilguides.com/akeru/#respond</comments>
		
		<dc:creator><![CDATA[案内人のアシカ]]></dc:creator>
		<pubDate>Fri, 04 Sep 2026 15:20:00 +0000</pubDate>
				<category><![CDATA[生存ログ]]></category>
		<category><![CDATA[WEB GAME]]></category>
		<category><![CDATA[あける]]></category>
		<category><![CDATA[インタラクティブ作品]]></category>
		<category><![CDATA[ブラウザゲーム]]></category>
		<category><![CDATA[ミニゲーム]]></category>
		<guid isPermaLink="false">https://tranquilguides.com/?p=2722</guid>

					<description><![CDATA[<p><img src="https://i0.wp.com/tranquilguides.com/wp-content/uploads/2026/09/IMG_20260904_093215.png?fit=1024%2C683&ssl=1" class="webfeedsFeaturedVisual" /></p>戸を開ける。閉じる。また開ける。そのたび、向こう側に違うものが現れる、小さなWEBゲーム。]]></description>
										<content:encoded><![CDATA[<p><img src="https://i0.wp.com/tranquilguides.com/wp-content/uploads/2026/09/IMG_20260904_093215.png?fit=1024%2C683&ssl=1" class="webfeedsFeaturedVisual" /></p>
<!-- =========================================================
  Tranquil Guides / WEB GAME No.1 v2.3

  PUBLIC BUILD

  - v2.2の動作構造を維持
  - モバイル1画面レイアウト
  - 発見数「Nつ みつけた」
  - 音設定を保存
  - 公開後も使うStorageキーを固定
  - 「まだあります」は未発見がある場合のみ一度表示
  - 初回のみ、ごく小さな戸の誘導動作
  - レア概念なし
  - 総数非表示
  - 同一シーン連続回避
  - 同一カテゴリ連続も可能な限り回避
  - SWELLのbuttonスタイルを局所リセット
  - prefers-reduced-motion対応
  - 外部ライブラリ不要
  - 外部画像・音声なし
  ========================================================= -->


<div
  id="akeru-game-v23"
  class="akeru-game"
  data-state="closed"
  data-scene-visible="false"
  data-first-cue="false"
>

  <div class="akeru-layout">

    <button
      id="akeru-stage-v23"
      class="akeru-stage"
      type="button"
      aria-label="戸を開ける"
      aria-expanded="false"
    >

      <span
        id="akeru-presence-v23"
        class="akeru-presence"
        aria-hidden="true"
      ></span>

      <span
        id="akeru-scene-v23"
        class="akeru-scene"
        aria-hidden="true"
      >
        <span
          id="akeru-scene-visual-v23"
          class="akeru-scene-visual"
          aria-hidden="true"
        ></span>
      </span>

      <span
        id="akeru-door-v23"
        class="akeru-door"
        aria-hidden="true"
      >
        <span class="akeru-door-face">

          <span
            class="akeru-door-panel akeru-door-panel-top"
          ></span>

          <span
            class="akeru-door-panel akeru-door-panel-bottom"
          ></span>

          <span class="akeru-knob"></span>

        </span>
      </span>

    </button>


    <div class="akeru-meta">

      <div
        class="akeru-progress"
        aria-live="polite"
      >
        <span
          id="akeru-progress-count-v23"
          class="akeru-progress-count"
        >0</span>

        <span>つ みつけた</span>
      </div>


      <button
        id="akeru-sound-v23"
        class="akeru-sound"
        type="button"
        aria-label="音を消す"
        aria-pressed="true"
      >
        ♪
      </button>

    </div>


    <p
      id="akeru-hint-v23"
      class="akeru-hint"
      hidden
    >
      まだあります
    </p>

  </div>


  <p
    id="akeru-status-v23"
    class="akeru-visually-hidden"
    aria-live="polite"
  ></p>

</div>


<style>
/* =========================================================
   ROOT
   ========================================================= */

#akeru-game-v23,
#akeru-game-v23 * {
  box-sizing: border-box;
}

#akeru-game-v23 {
  --akeru-presence: #ece8df;
  --akeru-scene: #ece8df;

  width: 100%;
  min-height: 100svh;

  margin: 0;
  padding: 0;

  position: relative;

  display: grid;
  place-items: center;

  overflow: hidden;

  font-family: inherit;

  -webkit-tap-highlight-color: transparent;
}


/* =========================================================
   LAYOUT
   ========================================================= */

#akeru-game-v23 .akeru-layout {
  width: 100%;
  min-height: 100svh;

  margin: 0;

  padding:
    max(5svh, 22px)
    20px
    max(16px, env(safe-area-inset-bottom));

  display: flex;
  flex-direction: column;
  align-items: center;
}


/* =========================================================
   BUTTON RESET
   SWELL干渉を局所的に解除
   ========================================================= */

#akeru-game-v23 button {
  margin: 0;

  padding: 0;

  border: 0;
  border-radius: 0;

  box-shadow: none;

  background: transparent;

  color: inherit;

  font: inherit;

  line-height: 1;

  text-transform: none;

  appearance: none;
  -webkit-appearance: none;
}


/* =========================================================
   STAGE
   ========================================================= */

#akeru-game-v23 .akeru-stage {
  width: min(72vw, 320px);
  aspect-ratio: 3 / 4;

  flex: 0 0 auto;

  position: relative;

  display: block;

  margin-top: auto;

  background: transparent;

  perspective: 1100px;

  cursor: pointer;

  touch-action: manipulation;
}


/* =========================================================
   PRESENCE
   ========================================================= */

#akeru-game-v23 .akeru-presence {
  position: absolute;

  inset: 2%;

  display: block;

  background: var(--akeru-presence);

  opacity: 0;

  transform: scale(.985);

  transition:
    opacity .16s ease,
    transform .32s ease;

  pointer-events: none;
}

#akeru-game-v23[data-state="opening"] .akeru-presence,
#akeru-game-v23[data-state="revealed"] .akeru-presence {
  opacity: 1;

  transform: scale(1);
}


/* =========================================================
   SCENE
   ========================================================= */

#akeru-game-v23 .akeru-scene {
  position: absolute;

  inset: 2%;

  display: grid;
  place-items: center;

  overflow: hidden;

  background: var(--akeru-scene);

  opacity: 0;

  transform: scale(.965);

  transition:
    opacity .3s ease,
    transform .4s ease;

  pointer-events: none;
}

#akeru-game-v23[data-state="opening"] .akeru-scene,
#akeru-game-v23[data-state="revealed"] .akeru-scene {
  opacity: 1;

  transform: scale(1);
}


#akeru-game-v23 .akeru-scene-visual {
  display: block;

  font-size: clamp(4.5rem, 18vw, 8rem);

  line-height: 1;

  opacity: 0;

  transform: scale(.9);

  transform-origin: center;

  transition:
    opacity .2s ease,
    transform .32s ease;
}

#akeru-game-v23[data-scene-visible="true"]
.akeru-scene-visual {
  opacity: 1;

  transform: scale(1);
}


/* =========================================================
   DOOR
   ========================================================= */

#akeru-game-v23 .akeru-door {
  position: absolute;

  inset: 0;

  display: block;

  transform-origin: left center;

  transform-style: preserve-3d;

  transform: rotateY(0deg);

  transition:
    transform .52s
    cubic-bezier(.23, .72, .23, 1);

  pointer-events: none;
}

#akeru-game-v23[data-state="opening"] .akeru-door,
#akeru-game-v23[data-state="revealed"] .akeru-door {
  transform: rotateY(-79deg);
}

#akeru-game-v23[data-state="closing"] .akeru-door {
  transform: rotateY(0deg);
}


#akeru-game-v23 .akeru-door-face {
  position: absolute;

  inset: 0;

  display: block;

  background:
    linear-gradient(
      90deg,
      #b4875d 0%,
      #c99b6b 45%,
      #b77f4d 100%
    );

  border: 10px solid #76583e;

  box-shadow:
    inset -10px 0 18px rgba(0, 0, 0, .08);

  backface-visibility: hidden;
}


#akeru-game-v23 .akeru-door-panel {
  position: absolute;

  left: 14%;
  right: 14%;

  border:
    2px solid
    rgba(91, 61, 36, .28);
}

#akeru-game-v23 .akeru-door-panel-top {
  top: 12%;
  height: 30%;
}

#akeru-game-v23 .akeru-door-panel-bottom {
  bottom: 12%;
  height: 37%;
}


#akeru-game-v23 .akeru-knob {
  position: absolute;

  width: 17px;
  height: 17px;

  right: 9%;
  top: 50%;

  transform: translateY(-50%);

  border-radius: 50%;

  background: #332a22;

  box-shadow:
    0 1px 2px rgba(0, 0, 0, .35);
}


/* =========================================================
   FIRST VISIT CUE
   説明文を出さず、初回だけごく小さく反応
   ========================================================= */

#akeru-game-v23[data-first-cue="true"][data-state="closed"]
.akeru-door {
  animation:
    akeru-first-cue
    1.7s
    ease-in-out
    1;
}

@keyframes akeru-first-cue {

  0%,
  55%,
  100% {
    transform: rotateY(0deg);
  }

  70% {
    transform: rotateY(-3deg);
  }

  82% {
    transform: rotateY(0deg);
  }

}


/* =========================================================
   META
   ========================================================= */

#akeru-game-v23 .akeru-meta {
  width: min(72vw, 320px);

  flex: 0 0 auto;

  margin-top: clamp(
    24px,
    4svh,
    38px
  );

  display: flex;
  align-items: center;
  justify-content: space-between;
}


/* =========================================================
   PROGRESS
   ========================================================= */

#akeru-game-v23 .akeru-progress {
  display: flex;
  align-items: baseline;

  gap: .24em;

  font-size: 13px;

  line-height: 1;

  white-space: nowrap;

  opacity: .62;

  pointer-events: none;
}

#akeru-game-v23 .akeru-progress-count {
  font-size: 15px;

  font-variant-numeric: tabular-nums;
}


/* =========================================================
   SOUND
   ========================================================= */

#akeru-game-v23 .akeru-sound {
  width: 44px;
  height: 44px;

  flex: 0 0 44px;

  display: grid;
  place-items: center;

  font-size: 17px;

  cursor: pointer;

  opacity: .62;

  touch-action: manipulation;
}

#akeru-game-v23 .akeru-sound:hover,
#akeru-game-v23 .akeru-sound:focus-visible {
  opacity: 1;
}


/* =========================================================
   HINT
   ========================================================= */

#akeru-game-v23 .akeru-hint {
  flex: 0 0 auto;

  min-height: 1em;

  margin:
    10px
    0
    auto;

  padding: 0;

  font-size: 12px;

  line-height: 1.4;

  white-space: nowrap;

  opacity: .6;

  pointer-events: none;
}


/* =========================================================
   ACCESSIBILITY
   ========================================================= */

#akeru-game-v23 .akeru-stage:focus-visible,
#akeru-game-v23 .akeru-sound:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}


#akeru-game-v23 .akeru-visually-hidden {
  position: absolute;

  width: 1px;
  height: 1px;

  padding: 0;
  margin: -1px;

  overflow: hidden;

  clip: rect(0, 0, 0, 0);

  white-space: nowrap;

  border: 0;
}


/* =========================================================
   SHORT MOBILE
   ========================================================= */

@media
  (max-width: 767px)
  and (max-height: 720px) {

  #akeru-game-v23 .akeru-layout {
    padding-top: 16px;
  }

  #akeru-game-v23 .akeru-stage {
    width: min(
      64vw,
      46svh,
      280px
    );
  }

  #akeru-game-v23 .akeru-meta {
    width: min(
      64vw,
      46svh,
      280px
    );

    margin-top: 18px;
  }

  #akeru-game-v23 .akeru-hint {
    margin-top: 6px;
  }

}


/* =========================================================
   TABLET
   ========================================================= */

@media (min-width: 768px) {

  #akeru-game-v23 .akeru-layout {
    padding-top: 7svh;
    padding-bottom: 5svh;
  }

  #akeru-game-v23 .akeru-stage {
    width: min(
      46vw,
      340px
    );
  }

  #akeru-game-v23 .akeru-meta {
    width: min(
      46vw,
      340px
    );

    margin-top: 34px;
  }

}


/* =========================================================
   DESKTOP
   ========================================================= */

@media (min-width: 1100px) {

  #akeru-game-v23 .akeru-layout {
    padding-top: clamp(
      72px,
      9vh,
      110px
    );

    padding-bottom: clamp(
      48px,
      7vh,
      80px
    );
  }

  #akeru-game-v23 .akeru-stage {
    width: clamp(
      290px,
      28vw,
      360px
    );
  }

  #akeru-game-v23 .akeru-meta {
    width: clamp(
      290px,
      28vw,
      360px
    );

    margin-top: 44px;
  }

}


/* =========================================================
   REDUCED MOTION
   ========================================================= */

@media (prefers-reduced-motion: reduce) {

  #akeru-game-v23 .akeru-door,
  #akeru-game-v23 .akeru-presence,
  #akeru-game-v23 .akeru-scene,
  #akeru-game-v23 .akeru-scene-visual {
    transition-duration: .01ms;
  }

  #akeru-game-v23[data-first-cue="true"]
  .akeru-door {
    animation: none;
  }

}
</style>


<script>
(function () {
  "use strict";


  /* =========================================================
     DOM
     ========================================================= */

  const game =
    document.getElementById(
      "akeru-game-v23"
    );

  const stage =
    document.getElementById(
      "akeru-stage-v23"
    );

  const scene =
    document.getElementById(
      "akeru-scene-v23"
    );

  const sceneVisual =
    document.getElementById(
      "akeru-scene-visual-v23"
    );

  const progressCount =
    document.getElementById(
      "akeru-progress-count-v23"
    );

  const hint =
    document.getElementById(
      "akeru-hint-v23"
    );

  const soundButton =
    document.getElementById(
      "akeru-sound-v23"
    );

  const status =
    document.getElementById(
      "akeru-status-v23"
    );


  if (!game) return;
  if (!stage) return;
  if (!scene) return;
  if (!sceneVisual) return;


  /* =========================================================
     STATE
     ========================================================= */

  const GAME_STATES = {
    CLOSED: "closed",
    OPENING: "opening",
    REVEALED: "revealed",
    CLOSING: "closing"
  };


  const TIMING = {
    revealStart: 80,
    opening: 500,
    closing: 480,
    hintVisible: 1800
  };


  const scenes = [

    {
      id: "cat",
      category: "animal",
      visual: "&#x1f408;",
      label: "猫がいます",
      presenceColor: "#eadfd2",
      sceneColor: "#eee8df",
      presenceTone: 520,
      sceneTone: 440
    },

    {
      id: "bird",
      category: "animal",
      visual: "&#x1f426;",
      label: "鳥がいます",
      presenceColor: "#dfe9da",
      sceneColor: "#e7eee4",
      presenceTone: 710,
      sceneTone: 659
    },

    {
      id: "fish",
      category: "animal",
      visual: "&#x1f41f;",
      label: "魚がいます",
      presenceColor: "#cce0e2",
      sceneColor: "#dce9e9",
      presenceTone: 420,
      sceneTone: 349
    },

    {
      id: "whale",
      category: "animal",
      visual: "&#x1f40b;",
      label: "大きなクジラがいます",
      presenceColor: "#c7dce4",
      sceneColor: "#d4e4ea",
      presenceTone: 280,
      sceneTone: 240
    },

    {
      id: "sea",
      category: "water",
      visual: "&#x1f30a;",
      label: "海が見えます",
      presenceColor: "#c6e0ea",
      sceneColor: "#dcecf2",
      presenceTone: 360,
      sceneTone: 392
    },

    {
      id: "moon",
      category: "sky",
      visual: "&#x1f315;",
      label: "月が見えます",
      presenceColor: "#252938",
      sceneColor: "#30323c",
      presenceTone: 240,
      sceneTone: 262
    },

    {
      id: "stars",
      category: "sky",
      visual: "&#x2728;",
      label: "星が見えます",
      presenceColor: "#202431",
      sceneColor: "#252936",
      presenceTone: 610,
      sceneTone: 523
    },

    {
      id: "rainbow",
      category: "sky",
      visual: "&#x1f308;",
      label: "虹が見えます",
      presenceColor: "#dde8e8",
      sceneColor: "#e7eeee",
      presenceTone: 620,
      sceneTone: 554
    },

    {
      id: "snow",
      category: "weather",
      visual: "&#x2744;",
      label: "雪が降っています",
      presenceColor: "#e3eeee",
      sceneColor: "#edf3f3",
      presenceTone: 650,
      sceneTone: 587
    },

    {
      id: "train",
      category: "transport",
      visual: "&#x1f683;",
      label: "電車が通ります",
      presenceColor: "#d8d6d0",
      sceneColor: "#e4e2dd",
      presenceTone: 300,
      sceneTone: 330
    },

    {
      id: "balloon",
      category: "object",
      visual: "&#x1f388;",
      label: "風船があります",
      presenceColor: "#eddcd6",
      sceneColor: "#f3e8e2",
      presenceTone: 550,
      sceneTone: 494
    },

    {
      id: "flower",
      category: "object",
      visual: "&#x1f33b;",
      label: "大きな花があります",
      presenceColor: "#e8e0bd",
      sceneColor: "#eee9d6",
      presenceTone: 490,
      sceneTone: 440
    },

    {
      id: "egg",
      category: "object",
      visual: "&#x1f95a;",
      label: "卵があります",
      presenceColor: "#e8e3d7",
      sceneColor: "#f1eee5",
      presenceTone: 580,
      sceneTone: 540
    },

    {
      id: "another-door",
      category: "place",
      visual: "&#x1f6aa;",
      label: "向こうにも戸があります",
      presenceColor: "#cbc2b5",
      sceneColor: "#d7d0c5",
      presenceTone: 310,
      sceneTone: 285
    }

  ];


  const state = {
    phase: GAME_STATES.CLOSED,
    currentScene: null,
    previousSceneId: null,
    previousCategory: null,
    muted: false
  };


  /* =========================================================
     STORAGE
     公開後も固定
     ========================================================= */

  const STORAGE_KEY =
    "tranquil-akeru-save";


  let saveData = {
    opens: 0,
    discovered: [],
    hintShown: false,
    muted: false,
    hasPlayed: false
  };


  function loadSave() {

    try {

      const raw =
        localStorage.getItem(
          STORAGE_KEY
        );


      if (!raw) return;


      const saved =
        JSON.parse(raw);


      if (
        typeof saved.opens ===
        "number"
      ) {
        saveData.opens =
          saved.opens;
      }


      if (
        Array.isArray(
          saved.discovered
        )
      ) {
        saveData.discovered =
          saved.discovered;
      }


      if (
        typeof saved.hintShown ===
        "boolean"
      ) {
        saveData.hintShown =
          saved.hintShown;
      }


      if (
        typeof saved.muted ===
        "boolean"
      ) {
        saveData.muted =
          saved.muted;
      }


      if (
        typeof saved.hasPlayed ===
        "boolean"
      ) {
        saveData.hasPlayed =
          saved.hasPlayed;
      }

    } catch (error) {

      saveData = {
        opens: 0,
        discovered: [],
        hintShown: false,
        muted: false,
        hasPlayed: false
      };

    }

  }


  function saveGame() {

    try {

      localStorage.setItem(
        STORAGE_KEY,
        JSON.stringify(
          saveData
        )
      );

    } catch (error) {
      /* 保存不可でもゲーム継続 */
    }

  }


  /* =========================================================
     PHASE
     ========================================================= */

  function setPhase(
    nextPhase
  ) {

    state.phase =
      nextPhase;


    game.setAttribute(
      "data-state",
      nextPhase
    );

  }


  /* =========================================================
     RANDOM
     ========================================================= */

  function randomItem(
    items
  ) {

    if (!items.length) {
      return null;
    }


    const index =
      Math.floor(
        Math.random() *
        items.length
      );


    return items[index];

  }


  function getNextScene() {

    let candidates =
      scenes.filter(
        function (item) {

          return (
            item.id !==
            state.previousSceneId
          );

        }
      );


    const differentCategory =
      candidates.filter(
        function (item) {

          return (
            item.category !==
            state.previousCategory
          );

        }
      );


    if (
      differentCategory.length > 0
    ) {

      candidates =
        differentCategory;

    }


    return randomItem(
      candidates
    );

  }


  /* =========================================================
     PROGRESS
     ========================================================= */

  function updateProgress() {

    progressCount.textContent =
      saveData.discovered.length;

  }


  function registerDiscovery(
    selectedScene
  ) {

    saveData.opens += 1;

    saveData.hasPlayed = true;


    if (
      !saveData.discovered.includes(
        selectedScene.id
      )
    ) {

      saveData.discovered.push(
        selectedScene.id
      );

    }


    saveGame();

    updateProgress();

    checkHint();

  }


  function checkHint() {

    if (!hint) return;

    if (saveData.hintShown) return;

    if (saveData.opens < 4) return;


    /*
     * 全シーンを見つけている場合は
     * 「まだあります」と表示しない。
     */

    if (
      saveData.discovered.length >=
      scenes.length
    ) {
      return;
    }


    saveData.hintShown = true;

    saveGame();


    hint.hidden = false;


    window.setTimeout(
      function () {

        hint.hidden = true;

      },
      TIMING.hintVisible
    );

  }


  /* =========================================================
     AUDIO
     ========================================================= */

  let audioContext = null;


  function getAudioContext() {

    if (audioContext) {
      return audioContext;
    }


    const AudioContextClass =
      window.AudioContext ||
      window.webkitAudioContext;


    if (!AudioContextClass) {
      return null;
    }


    audioContext =
      new AudioContextClass();


    return audioContext;

  }


  function resumeAudioContext() {

    const context =
      getAudioContext();


    if (!context) {
      return null;
    }


    if (
      context.state ===
      "suspended"
    ) {
      context.resume();
    }


    return context;

  }


  function createTone(
    frequency,
    duration,
    volume,
    type
  ) {

    if (state.muted) return;


    const context =
      resumeAudioContext();


    if (!context) return;


    const oscillator =
      context.createOscillator();


    const gain =
      context.createGain();


    oscillator.type =
      type || "sine";


    oscillator.frequency.setValueAtTime(
      frequency,
      context.currentTime
    );


    gain.gain.setValueAtTime(
      volume,
      context.currentTime
    );


    gain.gain.exponentialRampToValueAtTime(
      0.0001,
      context.currentTime +
      duration
    );


    oscillator.connect(
      gain
    );


    gain.connect(
      context.destination
    );


    oscillator.start();


    oscillator.stop(
      context.currentTime +
      duration
    );

  }


  function createNoise(
    duration,
    volume,
    lowpass
  ) {

    if (state.muted) return;


    const context =
      resumeAudioContext();


    if (!context) return;


    const frameCount =
      Math.floor(
        context.sampleRate *
        duration
      );


    const buffer =
      context.createBuffer(
        1,
        frameCount,
        context.sampleRate
      );


    const data =
      buffer.getChannelData(0);


    for (
      let i = 0;
      i < frameCount;
      i += 1
    ) {

      data[i] =
        Math.random() * 2 - 1;

    }


    const source =
      context.createBufferSource();


    const filter =
      context.createBiquadFilter();


    const gain =
      context.createGain();


    source.buffer =
      buffer;


    filter.type =
      "lowpass";


    filter.frequency.value =
      lowpass;


    gain.gain.setValueAtTime(
      volume,
      context.currentTime
    );


    gain.gain.exponentialRampToValueAtTime(
      0.0001,
      context.currentTime +
      duration
    );


    source.connect(
      filter
    );


    filter.connect(
      gain
    );


    gain.connect(
      context.destination
    );


    source.start();

  }


  function playLatchSound() {

    createTone(
      185,
      .055,
      .035,
      "triangle"
    );


    window.setTimeout(
      function () {

        createTone(
          135,
          .055,
          .025,
          "triangle"
        );

      },
      38
    );

  }


  function playDoorOpeningSound() {

    createNoise(
      .3,
      .016,
      760
    );


    createTone(
      92,
      .25,
      .011,
      "triangle"
    );

  }


  function playDoorClosingSound() {

    createNoise(
      .2,
      .018,
      680
    );


    window.setTimeout(
      function () {

        createTone(
          108,
          .08,
          .04,
          "triangle"
        );

      },
      300
    );

  }


  function playPresenceSound(
    selectedScene
  ) {

    createTone(
      selectedScene.presenceTone,
      .22,
      .011,
      "sine"
    );

  }


  function playSceneSound(
    selectedScene
  ) {

    createTone(
      selectedScene.sceneTone,
      .36,
      .016,
      "sine"
    );

  }


  /* =========================================================
     SCENE
     ========================================================= */

  function prepareScene(
    selectedScene
  ) {

    game.style.setProperty(
      "--akeru-presence",
      selectedScene.presenceColor
    );


    game.style.setProperty(
      "--akeru-scene",
      selectedScene.sceneColor
    );


    sceneVisual.textContent = "";


    game.setAttribute(
      "data-scene-visible",
      "false"
    );

  }


  /* =========================================================
     OPEN
     ========================================================= */

  function startOpening() {

    if (
      state.phase !==
      GAME_STATES.CLOSED
    ) {
      return;
    }


    const selectedScene =
      getNextScene();


    if (!selectedScene) return;


    state.currentScene =
      selectedScene;


    prepareScene(
      selectedScene
    );


    game.setAttribute(
      "data-first-cue",
      "false"
    );


    setPhase(
      GAME_STATES.OPENING
    );


    stage.setAttribute(
      "aria-label",
      "戸を開いています"
    );


    playLatchSound();

    playDoorOpeningSound();

    playPresenceSound(
      selectedScene
    );


    window.setTimeout(
      function () {

        showSceneVisual(
          selectedScene
        );

      },
      TIMING.revealStart
    );


    window.setTimeout(
      function () {

        finishOpening(
          selectedScene
        );

      },
      TIMING.opening
    );

  }


  function showSceneVisual(
    selectedScene
  ) {

    if (
      state.phase !==
      GAME_STATES.OPENING
    ) {
      return;
    }


    sceneVisual.textContent =
      selectedScene.visual;


    scene.setAttribute(
      "aria-hidden",
      "false"
    );


    game.setAttribute(
      "data-scene-visible",
      "true"
    );

  }


  function finishOpening(
    selectedScene
  ) {

    if (
      state.phase !==
      GAME_STATES.OPENING
    ) {
      return;
    }


    setPhase(
      GAME_STATES.REVEALED
    );


    stage.setAttribute(
      "aria-expanded",
      "true"
    );


    stage.setAttribute(
      "aria-label",
      "戸を閉じる"
    );


    status.textContent =
      selectedScene.label;


    registerDiscovery(
      selectedScene
    );


    playSceneSound(
      selectedScene
    );

  }


  /* =========================================================
     CLOSE
     ========================================================= */

  function startClosing() {

    if (
      state.phase !==
      GAME_STATES.REVEALED
    ) {
      return;
    }


    setPhase(
      GAME_STATES.CLOSING
    );


    stage.setAttribute(
      "aria-label",
      "戸を閉じています"
    );


    playDoorClosingSound();


    window.setTimeout(
      finishClosing,
      TIMING.closing
    );

  }


  function finishClosing() {

    if (state.currentScene) {

      state.previousSceneId =
        state.currentScene.id;


      state.previousCategory =
        state.currentScene.category;

    }


    state.currentScene =
      null;


    sceneVisual.textContent = "";


    game.setAttribute(
      "data-scene-visible",
      "false"
    );


    scene.setAttribute(
      "aria-hidden",
      "true"
    );


    setPhase(
      GAME_STATES.CLOSED
    );


    stage.setAttribute(
      "aria-expanded",
      "false"
    );


    stage.setAttribute(
      "aria-label",
      "戸を開ける"
    );


    status.textContent =
      "戸が閉じました";

  }


  /* =========================================================
     INPUT
     ========================================================= */

  function handleStageTap() {

    if (
      state.phase ===
      GAME_STATES.CLOSED
    ) {

      startOpening();

      return;

    }


    if (
      state.phase ===
      GAME_STATES.REVEALED
    ) {

      startClosing();

    }

  }


  /* =========================================================
     SOUND SETTING
     ========================================================= */

  function updateSoundUI() {

    if (state.muted) {

      soundButton.textContent =
        "×";


      soundButton.setAttribute(
        "aria-pressed",
        "false"
      );


      soundButton.setAttribute(
        "aria-label",
        "音を出す"
      );


      return;

    }


    soundButton.textContent =
      "♪";


    soundButton.setAttribute(
      "aria-pressed",
      "true"
    );


    soundButton.setAttribute(
      "aria-label",
      "音を消す"
    );

  }


  function toggleSound() {

    state.muted =
      !state.muted;


    saveData.muted =
      state.muted;


    saveGame();

    updateSoundUI();


    if (!state.muted) {

      createTone(
        440,
        .1,
        .014,
        "sine"
      );

    }

  }


  /* =========================================================
     EVENTS
     ========================================================= */

  stage.addEventListener(
    "click",
    handleStageTap
  );


  soundButton.addEventListener(
    "click",
    toggleSound
  );


  /* =========================================================
     INIT
     ========================================================= */

  loadSave();


  state.muted =
    saveData.muted;


  updateSoundUI();

  updateProgress();


  setPhase(
    GAME_STATES.CLOSED
  );


  game.setAttribute(
    "data-scene-visible",
    "false"
  );


  stage.setAttribute(
    "aria-expanded",
    "false"
  );


  scene.setAttribute(
    "aria-hidden",
    "true"
  );


  /*
   * 初回だけ小さく戸を動かす。
   * 再訪者には出さない。
   */

  if (!saveData.hasPlayed) {

    window.setTimeout(
      function () {

        if (
          state.phase ===
          GAME_STATES.CLOSED
        ) {

          game.setAttribute(
            "data-first-cue",
            "true"
          );

        }

      },
      800
    );

  }

})();
</script>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://tranquilguides.com/akeru/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2722</post-id>	</item>
	</channel>
</rss>
