CYOA Maker logo

CYOA Maker

Choose Your Own Adventure — Web Application Project Report

By Evan Prael
Web Tech 10, 2025–2026

1. Introduction

1.1 What is CYOA Maker?

CYOA Maker is a web application that lets users create and play Choose Your Own Adventure stories — interactive, branching narratives where the reader makes choices that determine how the story unfolds. Think of it as a lightweight, browser-based tool for writing the kind of adventure books that were popular in the 1980s and 1990s, but published on the web and playable by anyone with a browser.

A story is made up of story points — individual scenes or moments in the narrative. Each story point can have one or more choices that link to other story points, creating a web of possible paths through the story. Authors control the flow, and players experience a different adventure depending on the decisions they make.

Homepage screenshot showing story gallery

1.2 Purpose and Goals

This project was built as a Grade 12 Web Technologies assignment. The goal was to design and build a complete, functional, multi-user web application from scratch — covering everything from database design and server-side PHP logic to front-end styling and user experience.

Key goals included:

Table of Contents

2. Features & How It Works

2.1 Browsing and Playing Stories

The homepage (index.php) displays a gallery of all published stories as cards. Each card shows the story's title, a short description, the author's name, creation date, and a thumbnail image. No login is required to browse or play — the app is open to anyone.

Clicking a story launches the player (play.php). The player renders the current story point — its title, description text, and optional image — along with clickable choice buttons that advance the story. Players navigate the branching path until they reach an ending (a story point with no choices).

Logged-in users also see a "My Stories" filter on the homepage to quickly find their own work, and a "Clone" button that lets them copy any story into their own account as a starting point for a new one.

2.2 Creating and Editing Stories

Registered users can create new stories through the editor (editor.php). The editor has three distinct views, all handled within a single file:

  1. Story overview — shows all story points in the story with options to add, edit, or delete them.
  2. Story properties form — sets the story's title, description, cover image, theme, and layout.
  3. Story point form — writes the narrative text, uploads an image, sets a hint tooltip, and defines the branching choices for that story point.
Screenshot: story overview in editor — list of story points
Screenshot: story point editor form — text, image upload, and choices

Choices are added dynamically on the story point form. Each choice has a label (the button text the player sees) and a destination (which story point it leads to). Authors can add, reorder, and remove choices before saving.

Cloning a Story

Any logged-in user can clone any story on the homepage. Cloning creates a full, independent copy under the user's account — including all story points and choices — with a new title. All internal choice links are automatically remapped to the new copy's story points, so the cloned story works correctly from the start.

2.3 Themes and Layouts

Each story has a theme that controls the visual style of the play page. There are five themes to choose from:

In addition to the theme, authors can choose one of three image layouts for how story point images are positioned on the play page: image_left, image_right, or image_top.

Screenshot: side-by-side comparison of two different themes on the play page
Screenshot: account/profile page

2.4 User Accounts and Profiles

Users register with their name, email address, and a password. After logging in, they can visit their account page (account.php) to update their profile picture. Passwords are never stored in plain text — they are hashed using the industry-standard bcrypt algorithm before being saved.

Screenshot: admin panel showing user management table

2.5 Admin Panel

Users flagged as administrators see an extra panel on the account page. The admin panel provides the following capabilities:

The main admin account is defined in config.php using the MAIN_ADMIN constant, which prevents that account from being accidentally demoted or deleted.

Screenshot: admin panel showing user management table

2.6 Email Notifications

The app uses PHPMailer with Gmail SMTP to send two types of emails:

Screenshot: email welcome template
Table of Contents

3. Technical Breakdown

3.1 Tech Stack Overview

LayerTechnologyNotes
Backend languagePHP (procedural)No framework — plain PHP files
DatabaseMySQL (mysqli)Hosted database server; queries use prepared statements
EmailPHPMailer + Gmail SMTPFor welcome emails and password resets
Front-endVanilla JavaScriptNo frameworks (no React, jQuery, etc.)
FontsInter + Crimson TextLoaded via Google Fonts
DB Admin ToolphpMyAdminBrowser-based MySQL admin tool

3.2 Directory Structure

The project is organized as a single web root folder. Here are the key files and folders:

cyoa_maker/
├── config.php            # DB path, SMTP config, MAIN_ADMIN constant
├── db_functions.php      # All database CRUD functions
├── index.php             # Homepage — story gallery
├── editor.php            # Story editor (3 views in one file)
├── play.php              # Story player — loads theme CSS dynamically
├── account.php           # User profile + admin panel
├── header.php            # Shared navigation bar (included on every page)
├── login.php             # Login form
├── register.php          # Registration form
├── forgot_password.php   # Send password reset email
├── reset_password.php    # Accept reset token, set new password
├── logout.php            # Destroy session + redirect
├── mail_helper.php       # PHPMailer wrapper function
│
├── styles/               # CSS files (split by page/component)
│   ├── styles.css        # Base styles, navbar, buttons, layout
│   ├── cards.css         # Story gallery cards
│   ├── forms.css         # Auth and editor forms
│   ├── editor.css        # Editor-specific layout
│   └── account.css       # Account and admin panel styles
│
├── report/               # folder of this report
│   ├── index.php         # this report
│   └── 01.png            # screen shots for report
│
├── themes/               # Per-story play-page themes
│   ├── egyptian_theme.css
│   ├── forest_theme.css
│   ├── scifi_theme.css
│   ├── ocean_theme.css
│   └── desert_theme.css
│
├── images/
│   ├── {storyID}/        # Cover and story point images (one folder per story)
│   └── profiles/         # User profile images
│
├── phpmailer/            # PHPMailer library files
│
└── template/
    └── welcome.html      # Welcome email HTML template

3.3 CSS Architecture

Base Styles

The styles/ folder contains five CSS files, each focused on a different part of the app. styles.css provides the foundation — global typography, the navigation bar, buttons, and the overall page layout. The other files add styles specific to their component: gallery cards, forms, the editor layout, and the account panel. This split keeps each file manageable and avoids one giant stylesheet.

Theme System

The play page (play.php) is styled entirely differently from the rest of the app — each story has its own theme. When a player opens a story, play.php reads the story's saved theme name (e.g., forest) and dynamically builds a <link> tag pointing to themes/forest_theme.css.

All five theme files define the same set of CSS classes (e.g., .container, .choices, .banner), so the play page HTML never changes — only the stylesheet swaps out. Image layout variants (image_left, image_right, image_top) are applied as a class on the <body> tag and handled within each theme file.

3.4 Database Design

Why MySQL?

The project uses MySQL as its database, accessed via PHP's mysqli extension. MySQL is the standard database for shared PHP hosting environments and was taught in class, making it the natural choice. All queries use prepared statements with bound parameters to prevent SQL injection.

phpMyAdmin

phpMyAdmin is a browser-based admin tool for MySQL databases. It is typically pre-installed on shared hosting control panels (cPanel) and in local stacks like XAMPP. It allows developers to inspect tables, run SQL queries, and view or edit records without needing a separate desktop tool.

Tables

All tables use the prefix cyoam1_. There are five tables:

TablePurpose
cyoam1_usersStores registered user accounts (name, email, hashed password, admin flag, profile image)
cyoam1_storiesStores stories (title, description, cover image, theme, layout, author)
cyoam1_storypointsStores individual scenes within a story (title, narrative text, image, hint)
cyoam1_choicesStores the branching choices for each story point (button label + destination story point)
cyoam1_password_resetsStores temporary password-reset tokens with a 1-hour expiry

Entity Relationships

The data has a clear hierarchy:

Users (1) ──── (N) Stories (1) ──── (N) StoryPoints (1) ──── (N) Choices
                                                                        │
                                                     destinationID ─────┘
                                                     (links to another StoryPoint)

The first story point added to a story (lowest storypointID) is automatically treated as the starting point when a player begins the story.

3.5 Key PHP Patterns

Single-File Page Routing

Each page is a self-contained PHP file. Pages that handle form submissions do so at the very top of the file using a switch statement on a hidden action field in the form. After processing, the page immediately redirects with header('Location: ...') — this is called the Post/Redirect/Get pattern and prevents duplicate form submissions on browser refresh.

Flash Messages

After a redirect, the page needs a way to show the user a success or error message. This is done using PHP sessions: before redirecting, the action stores a message in $_SESSION['flash_message'] or $_SESSION['flash_error']. The destination page reads the message, displays it, and immediately removes it from the session so it only appears once.

Database Connection Singleton

All database access goes through a single function, db_connect(), which uses a static local variable to ensure only one mysqli connection is ever created per request. Tables must be created on the server in advance using the provided SQL schema file.

3.6 Security Practices

Security was considered throughout the project. Key measures include:

Table of Contents

4. Deployment Guide

4.1 Prerequisites

CYOA Maker requires a standard PHP web server environment:

Note: For local development, XAMPP includes Apache, PHP, and MySQL out of the box — no extra configuration needed.

For local development, XAMPP or Laragon work well out of the box — both include MySQL and phpMyAdmin pre-installed.

4.2 Setting Up from a ZIP File

  1. Extract the ZIP file into your web server's document root (e.g., htdocs/cyoa_maker/ for XAMPP).
  2. Open config.php and update the settings (see Section 4.3 below).
  3. Ensure the folder is writable by the web server (chmod 755 on Linux/macOS, or check folder permissions on Windows).
  4. Navigate to the app in your browser (e.g., http://localhost/cyoa_maker/).
  5. Create a MySQL database and import the provided schema SQL file using phpMyAdmin (or the MySQL command line) to create all five tables.
  6. Register the first account and then manually set isAdmin = 1 for that user in phpMyAdmin, or set the email in MAIN_ADMIN in config.php before registering.

4.3 Important Configuration

All key settings live in config.php:

SettingWhat It Does
DB_HOST, DB_USER, DB_PASSWORD, DB_NAMEMySQL connection credentials for the database server
MAIN_ADMINEmail address of the protected super-admin account (cannot be demoted or deleted)
SMTP settingsGmail address and app password used for sending emails via PHPMailer
Table of Contents

5. Development Reflections

5.1 AI-Assisted Development

This project made heavy use of AI coding tools throughout every phase of development, from the initial project proposal through implementation and all the way to writing this report. Development began with GitHub Copilot, which was used for the early stages of the project. After the monthly Copilot credits ran out, the project switched to Claude Code, which was used for the remainder of development and documentation.

In the proposal phase, AI was used to brainstorm project ideas, refine the concept, and help structure the formal project proposal document.

During implementation, AI served as a coding assistant — generating initial code for features, explaining patterns, helping debug problems, and suggesting improvements. Rather than writing every line manually, the workflow became more about describing what was needed, reviewing the generated code, testing it, and adjusting. This significantly accelerated development while still requiring a solid understanding of the underlying technology to evaluate the output correctly.

For documentation and reporting (including this report), AI helped structure content, fill in technical details, and ensure completeness. The final output still required review and editing — AI-generated writing needs human judgment to be accurate and appropriately scoped.

Overall, AI tools felt like having a knowledgeable collaborator available at all times. The key skill was learning how to communicate clearly with the AI — giving it enough context, pushing back when the output was wrong, and knowing when to accept a suggestion versus rewriting it. A selection of the prompts used throughout this project is included in Appendix A.

5.2 Deploying to a Shared Host

Moving the project from a local XAMPP setup to a real shared hosting server was straightforward. The server runs Apache with PHP and MySQL, which matches the local development environment, so no code changes were needed.

The main step was migrating the data: the local database was exported using phpMyAdmin, and the resulting SQL file was imported into the server's database through the hosting control panel's phpMyAdmin interface. The config.php file was then updated with the server's database credentials and the live URL, and the app was ready.

5.3 Lessons Learned

Table of Contents

6. Future Enhancements

The current version of CYOA Maker covers the core create-and-play experience, but there are several features that would make the platform significantly richer:

User Comments and Feedback

Adding a comment section to the story play page would let players leave feedback for authors. This would require a new cyoam1_comments table linked to stories and users, a comment form on play.php, and a moderation option in the admin panel.

Story Ratings (1–5 Stars)

A star rating system would help players discover the best stories in the gallery. Each registered user could rate a story once, and the average rating would be displayed on the gallery card. This would add a social element that encourages authors to put care into their stories.

AI Integration

Given how central AI was to the development process, integrating AI features directly into the app itself is a natural next step. Possible AI-powered features include:

Table of Contents

7. Acknowledgements

This project was completed as part of the Grade 12A Web Technologies 10 course, 2025–2026.

Table of Contents

Appendix A: Sample Prompts

One challenge with AI-assisted development is that it is hard to track entire conversations in the VS Code chat window — sessions get long, context gets lost, and there is no easy export. To work around this, many of the bigger feature requests were pre-written as plain text prompts before being pasted into the chat. A selection of those prompts is shown below.

I want to note that the initial project proposal was also used as a giant prompt and steering document for the AI — it was provided at the start of the project so the AI had full context about the app's goals, features, and requirements before any code was written. The prompts below are individual feature requests that were added on top of that.

Account & Admin Management Page

This prompt was used to build the entire account.php page from scratch.

Prompt
I need a page where logged-in users can manage their own profile (profile image and password only), and where admin users can manage all users — add, list, edit, and delete them. The link in the navigation bar should say "Account" for regular users and "Admin" for admins. Non-admin view: - Show the user's current profile info (name, email, profile image) - Name and email are read-only — users can't change these after registering - Users can update their password or profile image Admin view: - Show a table of all users with columns for: profile image thumbnail, email, first name, last name, and whether they are an admin - Include Edit and Delete buttons for each row - Clicking Edit should open a popup form with all the user's details, including a checkbox to set them as admin Please ask if you need any other details before starting.

Cascade Deletes

Ensuring that deleting a user or story also cleans up all related images and database records.

Prompt
When a story point is deleted, also delete its image file. When a story is deleted, delete all of its story points and their images. When an admin deletes a user, delete that user's profile image and all of their stories. Also, in the story editor, move the Delete Story button to the far right so it is separated from the Play and Edit Properties buttons.

Clone Story Feature

Adding the ability to duplicate one's own stories.

Prompt
Add a Clone Story button to the My Stories page, to the right of the Play button. When clicked: - Show a popup asking for the title of the cloned story, defaulting to the original title - When confirmed, create a full copy of the story and all its story points in the database - Create a new image folder for the copy and copy all images from the original folder into it - Make sure all the choices in the cloned story still link to the correct (new) story points This feature should only be available to logged-in users, and only for their own stories.

Clone Story — Loading Spinner

The clone operation caused the page to freeze while files were being copied.

Prompt
When cloning a story, the page freezes temporarily while images are being copied to the new folder. Can you show a simple spinner so the user knows something is happening? Please keep the code simple — don't add a lot of extra code just for this.

Story Card Thumbnail Links

Prompt
On the story cards on the homepage, make the cover image clickable so it links to the play page — the same as the Play button.

Image Layout Feature

Adding a layout selector to the story properties so authors can control how images appear during playback.

Prompt
Add a Layout dropdown to the story properties editor, placed below the theme dropdown. The options are: - Image Left — the current default layout, with the image on the left and text on the right - Image Right — the same layout but flipped - Image Top — the image spans the full width across the top, with the story text below it and the choices displayed horizontally in a row at the bottom. This is designed for wide images. The mobile layout should always switch to a single column regardless of which option is chosen. Let me know if you need any other details before starting.

Move CSS Files to a Subfolder

Prompt
Move all the CSS files into a subfolder called styles/.

Logo Circle in Header

Prompt
Add a CSS circle around the logo image in header.php. Use a medium gray border and a light gray gradient as the background.

Author Credit on Play Page

Prompt
On the story play page, replace the "a choose-your-own-adventure story" text that appears under the story title with "by" followed by the username of the person who created the story.

Image Preview — All Upload Areas

Prompt
When selecting an image in the story point editor, a preview appears before uploading. This doesn't seem to work on the registration page when a user selects a profile image. Please check all the places in the app where images can be uploaded and make sure the preview works consistently everywhere.

Admin — Reassign Story Ownership

Prompt
Give admins the ability to reassign a story to a different user. When an admin is on the Edit Story Properties screen, show a dropdown of all registered users defaulting to the current owner. Place this dropdown between the description field and the theme dropdown.

Per-Story-Point "Go Back" Toggle

Adding the ability to disable the automatic Go Back button on individual story points.

Prompt
The story play page currently adds an automatic "Go Back" button to every story point. I want to be able to turn this off for individual story points. I have already added a column called enable_autoBack_nav to the story points table and set it to 1 for all existing records. In the story point editor, add a checkbox labelled "Enable automatic back navigation" at the bottom of the Choices section. It should be checked by default for new story points. Only show this checkbox when at least one choice has already been added.

Play Button Inside Story Point Editor

Prompt
Add a Play button to the top right corner of the story point editor form, styled the same as the existing Play button elsewhere in the editor. It should open the story playback page in a new browser tab.

Project Documentation & Report

The prompt used to start building this report.

Prompt
I want to create a project report with a clickable table of contents. Here are some starter ideas for sections — feel free to reorganize, add, or rename them: - Overview: introduction to the app, how gameplay works, admin features, accounts and email notifications - Technical breakdown: directory structure, CSS and theme files, database design (MySQL, phpMyAdmin, tables) - Deployment: prerequisites, setup from a ZIP file, important configuration settings - Summary: what I learned (AI-assisted development, deploying to a shared host), possible future features (comments, ratings, AI integration) Include placeholder spots for screenshots wherever they make sense. The table of contents should have clickable links to each section. The output can be a markdown file for now — I might convert it to HTML later.
Table of Contents