0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【GAS×Netlify】ブラウザで除夜の鐘を突き放題にする「Web除夜の鐘」を爆速で作った話

Posted at

はじめに

こんにちは。
今日は2025年12月31日、大晦日ですね。

みなさん、煩悩は溜まっていますか? 私は溜まっています。

一年の締めくくりといえば「除夜の鐘」。
108つの煩悩を祓うために鐘を突きに行きたいところですが、外は寒いし、近所のお寺は並んでいるかもしれない……。

「じゃあ、ブラウザで突き放題にすればいいじゃない」

そう思い立ち、みんなが投稿した煩悩を、鐘の音とともに浄化できるWebアプリを突貫工事で開発しました。
技術構成は HTML/JS + Google Apps Script (GAS) + Netlify です。

作ったもの:除夜の鐘 - 煩悩ガチャ

百聞は一見に如かず。まずは音を出して突いてみてください。(※音が出ます)

🔔 アプリはこちら
https://joya-no-kane-2025-2026.netlify.app/
※注意: 本アプリは「除夜の鐘」という性質上、年が明けたら(1/1中には)サービスを停止する予定です。 今のうちに心ゆくまで鳴らしてください!

  • 鐘をクリック: 厳かな音とともに鐘が揺れ、誰かの「煩悩」がランダムで表示されて浄化(フェードアウト)されます。
  • 奉納機能: 自分の煩悩を投稿できます。投稿されたデータは即座にDB(スプレッドシート)に反映され、次の誰かの鐘つきで表示されます。
  • リアルタイムカウンター: 全世界で浄化された煩悩の総数がカウントされています。

本記事では、このアプリをどのように 爆速で実装・デプロイまで持っていったか を紹介します。

技術構成

今回は「とにかく早く公開する」「コストをかけない(無料)」を最優先にし、サーバーレス構成を採用しました。

  • Frontend: HTML5, CSS3, Vanilla JS (フレームワークなし)
  • Backend: Google Apps Script (GAS)
  • Database: Google Spreadsheets
  • Hosting: Netlify (Drag & Dropデプロイ)

アーキテクチャ

非常にシンプルです。クライアント(HTML)から fetch でGASのWebアプリURLを叩き、GASがスプレッドシートを読み書きしてJSONを返す仕組みです。

[ Browser ]  <--->  [ GAS (API) ]  <--->  [ Spreadsheet (DB) ]

実装のポイント:GASを簡易バックエンドにする

Google Apps Scriptは doGet 関数を定義して「Webアプリとして導入」することで、簡単にGETリクエストを受け付けるAPIエンドポイントになります。
今回はクエリパラメータ action の値によって処理を3つに分岐させました。

GAS側のコード(抜粋)

function doGet(e) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const action = e.parameter.action;

  // 1. 初期データ取得(煩悩リストと総カウント)
  if (action === 'getData') {
    const lastRow = sheet.getLastRow();
    // A列にある煩悩テキストを配列化して取得
    const bonnoList = sheet.getRange(1, 1, lastRow, 1).getValues().flat().filter(String);
    
    // 総回数はプロパティストア(高速なKVS的な領域)から取得
    const count = PropertiesService.getScriptProperties().getProperty('TOTAL_COUNT') || 0;
    
    return responseJSON({ bonnoList, count });
  }

  // 2. カウントアップ(鐘が突かれたとき)
  else if (action === 'tap') {
    const props = PropertiesService.getScriptProperties();
    // 現在値を数値化してインクリメント
    let count = parseInt(props.getProperty('TOTAL_COUNT')) || 0;
    count++;
    props.setProperty('TOTAL_COUNT', count);
    
    return responseJSON({ status: 'ok', count });
  }

  // 3. 煩悩の追加(投稿されたとき)
  else if (action === 'add') {
    const text = e.parameter.text;
    if (text) {
      sheet.appendRow([text]); // スプシの末尾に追加
      return responseJSON({ status: 'saved' });
    }
  }
}

// JSONを返すヘルパー関数
function responseJSON(data) {
  return ContentService.createTextOutput(JSON.stringify(data))
    .setMimeType(ContentService.MimeType.JSON);
}

※重要ポイント: GASをデプロイする際、「アクセスできるユーザー」を 「全員(Anyone)」 に設定しないと、外部から fetch した際にCORSエラーや403エラーになるので注意が必要です。

この30行程度のコードで、**「データの取得」「カウントの永続化」「新規データの書き込み」**というWebアプリに必要なCRUD(UとDはないですが)機能が揃います。

特に、カウント数はスプレッドシートのセルではなく PropertiesService(スクリプトプロパティ)に保存することで、読み書きの速度を上げ、排他制御の問題も軽減させています。

実装のポイント:フロントエンド

フロントエンドは素のJavaScriptです。
ユーザー体験(UX)を損なわないよう、APIのレスポンスを待たずにアニメーションや音を先行して再生させています。

// 鐘を突く処理
bellBtn.addEventListener('click', async () => {
    // 1. UI演出:アニメーションと音再生(即座に反応させる)
    bellBtn.classList.remove('shake');
    void bellBtn.offsetWidth; // リフロー発生でアニメーションリセット
    bellBtn.classList.add('shake');
    
    audio.currentTime = 0;
    audio.play();
    
    // 2. 煩悩の表示(ロード済みのリストからランダム選出)
    showRandomBonno();

    // 3. カウントアップ送信(非同期で裏側実行)
    try {
        // キャッシュ回避のために現在時刻(t)を付与
        await fetch(`${API_URL}?action=tap&t=${Date.now()}`);
    } catch (e) {
        console.error("送信エラー", e);
    }
});

ここでのハマりポイントは ブラウザのキャッシュ でした。
単純に同じURL(?action=tap)を叩き続けると、ブラウザが通信を省略してしまいGAS側のカウントが増えない現象が発生。
URLの末尾に &t=${Date.now()} をつけて毎回異なるリクエストに見せることで解決しました。

デザインと生成AI活用

見た目(デザイン)の実装も爆速化の鍵でした。
今回は、コードの修正案出しから画像生成まで、**生成AI(Gemini)**とペアプログラミングのような形で進めました。

鐘の素材

最初はCSSで簡単な台形を描画していましたが、「もっとリアルな鐘を突きたい」という欲が出ました。そこで、鐘の画像生成もAIに依頼しました。

私: 「日本の古いお寺にある、大きな青銅製の釣鐘(梵鐘)の画像を生成してください。苔むしていて、重厚感があるイラストで。正面の画像。背景は真っ黒で」

AI: 「承知しました。こちらはいかがでしょうか。(画像生成)」

Gemini_Generated_Image_pkzyttpkzyttpkzy (1).png

これで数秒でリアルな鐘の画像(bell.png)が手に入りました。これをHTMLに配置し、CSS Animationでクリック時に小刻みに振動させることで、打撃感を表現しています。

浄化のエフェクト

煩悩のテキストがフワッと消えるエフェクトも、AIと相談しながら調整しました。

/* 浄化のアニメーション */
@keyframes purify {
    0% {
        opacity: 0;
        transform: translateY(15px) scale(0.9);
        filter: blur(2px);
    }
    20% {
        opacity: 1;
        transform: translateY(0) scale(1);
        filter: blur(0);
    }
    100% {
        opacity: 0;
        /* 上に昇りながら拡大し、ぼやけて消える */
        transform: translateY(-30px) scale(1.1);
        filter: blur(10px);
    }
}

「ただ消えるだけでなく、浄化される感じで」とオーダーしたところ、transform で上昇させつつ、filter: blur() でぼかしを加えるアイデアが提案され、それがそのまま採用されています。

デプロイ:フォルダを投げるだけ

開発の最後、公開作業も時間をかけずに終わらせました。
今回はフレームワークを使っていない(生のHTML/JS)ため、ビルドプロセスは不要です。

Netlify Drop を使えば、文字通り「フォルダを投げるだけ」で公開されます。

  1. index.htmlbell.pngbell.mp3 を1つのフォルダにまとめる
  2. Netlifyの管理画面("Drag and drop your site output folder here")にフォルダごとドロップ
  3. 即座にHTTPSで公開完了

コマンドラインも設定ファイルも一切不要。
この手軽さが、締め切り(年越し)に追われる季節モノ開発には最強の味方でした。

さいごに

こうして、アイデア出しからデプロイまで、実質数時間で「Web除夜の鐘」が完成しました。

サーバーもDBも用意せず、Googleアカウントと静的ホスティングだけで動的なWebアプリが作れてしまう。GASのポテンシャルを改めて感じる開発でした。

奉納をお待ちしています

このアプリは、みなさんの煩悩データが集まることで完成します。
今年のうちに吐き出しておきたいことがあれば、ぜひ「奉納」していってください。匿名ですし、即座にサーバーに送られて、次の誰かの鐘つきで浄化されます。

🔔 鐘つきはこちらから(再掲)
https://joya-no-kane-2025-2026.netlify.app/
※冒頭にも書きましたが、明日1/1中にはサービスを停止してWeb上の鐘もつき納めとする予定です。 今年限りの煩悩、今のうちに全て浄化していってください!
それではみなさん、良いお年を!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?