CP_AUTOMATION/CognitivePrism/my-project/cognitive-docs/Doc/vpam_rules.md
2025-12-12 19:54:54 +05:30

73 KiB
Raw Blame History

This project implements a Visual Paired Associates Memory (VPAM) Test frontend application for psychological research. The test measures associative memory, encoding, retention, consolidation, and recognition abilities by teaching participants picture-picture pairs and testing recall at multiple time points.

Response Requirements

Every AI response must start with: "Inshallah lets start"

Modal Identification: At the beginning of each response, identify the current AI model being used by stating its official name.


🎯 Core Test Specifications

Test Structure (CRITICAL)

  • Total Pairs: 35 unique picture-picture pairs
  • Encoding Phase: Learn all 35 pairs (one-time exposure)
  • Immediate Cued Recall: Test immediately after encoding (35 trials)
  • Delay Interval: 15 minutes with filler tasks
  • Delayed Cued Recall: Test after delay (35 trials)
  • Recognition Phase: Multiple choice recognition (35 trials)
  • Total Time: Approximately 30-35 minutes

Age Groups

  1. Adolescents (14-18 years):

    • Exposure time: 3.5 seconds per pair
    • Recall response time: 15 seconds per trial
    • Recognition response time: 4 seconds per trial
    • Colorful gradient background
    • Friendly, encouraging instructions
    • Pairs: Pen+Kite, Butterfly+Spoon, Elephant+Mango, etc. (35 pairs)
  2. Adults (18-22 years):

    • Exposure time: 2.5 seconds per pair
    • Recall response time: 15 seconds per trial
    • Recognition response time: 4 seconds per trial
    • Soft neutral background
    • Professional, concise instructions
    • Pairs: Mixer+Spectacles, Mobile Phone+Water Tap, Lemon+Socks, etc. (35 pairs)

🔑 Critical Implementation Rules

Phase 1: Encoding (Learning Phase)

// ENCODING RULES:
// 1. Display 35 pairs sequentially
// 2. Each pair shown for 3.5s (adolescents) or 2.5s (adults)
// 3. NO response required - passive viewing only
// 4. Pairs displayed in WHITE BOX, centered on screen
// 5. Automatic progression to next pair
// 6. After last pair → immediately transition to Immediate Recall

// Pair Display Format:
// [Image 1] + [Image 2]
// Side by side or stacked (specify in design)

Adolescent Pairs (EXACT ORDER from PDF):

[
  { first: 'Pen', second: 'Kite' },
  { first: 'Butterfly', second: 'Spoon' },
  { first: 'Elephant', second: 'Mango' },
  { first: 'Lock', second: 'Flute' },
  { first: 'Notebook', second: 'Cow' },
  { first: 'Eraser', second: 'Cup' },
  { first: 'Tomato', second: 'Bag' },
  { first: 'Heart', second: 'Scissors' },
  { first: 'Drum', second: 'Goat' },
  { first: 'Phone', second: 'Auto' },
  { first: 'Table', second: 'Parrot' },
  { first: 'Robot', second: 'Chair' },
  { first: 'Ship', second: 'Potato' },
  { first: 'Pencil', second: 'Dice' },
  { first: 'Belt', second: 'Lemon' },
  { first: 'Hibiscus', second: 'Bike' },
  { first: 'Carrot', second: 'Peacock' },
  { first: 'Candle', second: 'Balloon' },
  { first: 'Fork', second: 'Bulb' },
  { first: 'Sharpener', second: 'Lotus' },
  { first: 'Shirt', second: 'Banana' },
  { first: 'Chain', second: 'Jug' },
  { first: 'Hand', second: 'Rat' },
  { first: 'Television', second: 'Coconut' },
  { first: 'Book', second: 'Key' },
  { first: 'Shoe', second: 'Ears' },
  { first: 'Dog', second: 'Crown' },
  { first: 'Cloud', second: 'Mirror' },
  { first: 'Cycle', second: 'Plate' },
  { first: 'Pig', second: 'Clock' },
  { first: 'Watch', second: 'Crayon' },
  { first: 'Leaf', second: 'Pant' },
  { first: 'Door', second: 'Apple' },
  { first: 'Hat', second: 'Ring' },
  { first: 'Bucket', second: 'Monkey' }
]

Adult Pairs (EXACT ORDER from PDF):

[
  { first: 'Mixer', second: 'Spectacles' },
  { first: 'Mobile Phone', second: 'Water Tap' },
  { first: 'Lemon', second: 'Socks' },
  { first: 'Plane', second: 'Lamp' },
  { first: 'Pillow', second: 'Mug' },
  { first: 'Bowl', second: 'Switch' },
  { first: 'Helmet', second: 'Rose' },
  { first: 'Shark', second: 'Fan' },
  { first: 'Sun', second: 'Table' },
  { first: 'Rock', second: 'Peas' },
  { first: 'Chilli', second: 'Hen' },
  { first: 'Window', second: 'Orange' },
  { first: 'Purse', second: 'Hammer' },
  { first: 'Zebra', second: 'Almonds' },
  { first: 'Ball', second: 'Nose' },
  { first: 'Rope', second: 'Ice Cream' },
  { first: 'Horse', second: 'Umbrella' },
  { first: 'Nose', second: 'Cauliflower' },
  { first: 'Pen', second: 'Chair' },
  { first: 'Clock', second: 'Dustbin' },
  { first: 'Laptop', second: 'Moon' },
  { first: 'Crocodile', second: 'Brush' },
  { first: 'Cup', second: 'Rainbow' },
  { first: 'Scooter', second: 'Calendar' },
  { first: 'Camel', second: 'Key' },
  { first: 'Bank', second: 'Watermelon' },
  { first: 'Jug', second: 'Beetle' },
  { first: 'Iron Box', second: 'Strawberry' },
  { first: 'Sunflower', second: 'Ladder' },
  { first: 'Screwdriver', second: 'Garlic' },
  { first: 'Basket', second: 'Train' },
  { first: 'Camera', second: 'Starfish' },
  { first: 'Lady\'s Finger', second: 'Candle' },
  { first: 'Torch Light', second: 'Mat' },
  { first: 'Cherry', second: 'Mirror' }
]

Phase 2: Immediate Cued Recall

// IMMEDIATE RECALL RULES:
// 1. Show ONE item from each learned pair as CUE
// 2. Participant types the MATCHING item
// 3. 15 seconds response window per trial
// 4. Text input field below cue image
// 5. Automatic advance on submit OR timeout
// 6. Show "Time is up!" for 1 second on timeout
// 7. NO feedback on correctness during test
// 8. Order: Show SECOND item, ask for FIRST (or vice versa)

// Response Validation:
// - Must match EXACT item name (case-insensitive)
// - Don't accept categories ("animal" vs "Elephant")
// - Don't accept descriptors ("red apple" vs "Apple")
// - Trim whitespace, ignore punctuation

Adolescent Immediate Recall Cues (EXACT ORDER):

[
  { cue: 'Kite', correctAnswer: 'Pen' },
  { cue: 'Butterfly', correctAnswer: 'Spoon' },
  { cue: 'Mango', correctAnswer: 'Elephant' },
  { cue: 'Flute', correctAnswer: 'Lock' },
  { cue: 'Notebook', correctAnswer: 'Cow' },
  { cue: 'Eraser', correctAnswer: 'Cup' },
  { cue: 'Bag', correctAnswer: 'Tomato' },
  { cue: 'Heart', correctAnswer: 'Scissors' },
  { cue: 'Drum', correctAnswer: 'Goat' },
  { cue: 'Auto', correctAnswer: 'Phone' },
  { cue: 'Parrot', correctAnswer: 'Table' },
  { cue: 'Robot', correctAnswer: 'Chair' },
  { cue: 'Potato', correctAnswer: 'Ship' },
  { cue: 'Dice', correctAnswer: 'Pencil' },
  { cue: 'Belt', correctAnswer: 'Lemon' },
  { cue: 'Bike', correctAnswer: 'Hibiscus' },
  { cue: 'Peacock', correctAnswer: 'Carrot' },
  { cue: 'Balloon', correctAnswer: 'Candle' },
  { cue: 'Bulb', correctAnswer: 'Fork' },
  { cue: 'Sharpener', correctAnswer: 'Lotus' },
  { cue: 'Banana', correctAnswer: 'Shirt' },
  { cue: 'Jug', correctAnswer: 'Chain' },
  { cue: 'Rat', correctAnswer: 'Hand' },
  { cue: 'Coconut', correctAnswer: 'Television' },
  { cue: 'Key', correctAnswer: 'Book' },
  { cue: 'Ears', correctAnswer: 'Shoe' },
  { cue: 'Dog', correctAnswer: 'Crown' },
  { cue: 'Mirror', correctAnswer: 'Cloud' },
  { cue: 'Plate', correctAnswer: 'Cycle' },
  { cue: 'Clock', correctAnswer: 'Pig' },
  { cue: 'Crayon', correctAnswer: 'Watch' },
  { cue: 'Pant', correctAnswer: 'Leaf' },
  { cue: 'Apple', correctAnswer: 'Door' },
  { cue: 'Ring', correctAnswer: 'Hat' },
  { cue: 'Monkey', correctAnswer: 'Bucket' }
]

Adult Immediate Recall Cues (EXACT ORDER):

[
  { cue: 'Spectacles', correctAnswer: 'Mixer' },
  { cue: 'Water Tap', correctAnswer: 'Mobile Phone' },
  { cue: 'Lemon', correctAnswer: 'Socks' },
  { cue: 'Plane', correctAnswer: 'Lamp' },
  { cue: 'Mug', correctAnswer: 'Pillow' },
  { cue: 'Switch', correctAnswer: 'Bowl' },
  { cue: 'Rose', correctAnswer: 'Helmet' },
  { cue: 'Shark', correctAnswer: 'Fan' },
  { cue: 'Table', correctAnswer: 'Sun' },
  { cue: 'Peas', correctAnswer: 'Rock' },
  { cue: 'Hen', correctAnswer: 'Chilli' },
  { cue: 'Orange', correctAnswer: 'Window' },
  { cue: 'Purse', correctAnswer: 'Hammer' },
  { cue: 'Zebra', correctAnswer: 'Almonds' },
  { cue: 'Nose', correctAnswer: 'Ball' },
  { cue: 'Ice Cream', correctAnswer: 'Rope' },
  { cue: 'Umbrella', correctAnswer: 'Horse' },
  { cue: 'Cauliflower', correctAnswer: 'Nose' },
  { cue: 'Chair', correctAnswer: 'Pen' },
  { cue: 'Dustbin', correctAnswer: 'Clock' },
  { cue: 'Moon', correctAnswer: 'Laptop' },
  { cue: 'Brush', correctAnswer: 'Crocodile' },
  { cue: 'Rainbow', correctAnswer: 'Cup' },
  { cue: 'Calendar', correctAnswer: 'Scooter' },
  { cue: 'Camel', correctAnswer: 'Key' },
  { cue: 'Watermelon', correctAnswer: 'Bank' },
  { cue: 'Beetle', correctAnswer: 'Jug' },
  { cue: 'Strawberry', correctAnswer: 'Iron Box' },
  { cue: 'Ladder', correctAnswer: 'Sunflower' },
  { cue: 'Garlic', correctAnswer: 'Screwdriver' },
  { cue: 'Train', correctAnswer: 'Basket' },
  { cue: 'Starfish', correctAnswer: 'Camera' },
  { cue: 'Candle', correctAnswer: 'Lady\'s Finger' },
  { cue: 'Mat', correctAnswer: 'Torch Light' },
  { cue: 'Mirror', correctAnswer: 'Cherry' }
]

Phase 3: Delay Interval (15 Minutes)

// DELAY INTERVAL RULES:
// 1. Duration: EXACTLY 15 minutes
// 2. Filler tasks administered sequentially:
//    a. Response Inhibition Task (Go/No-Go)
//    b. Cognitive Flexibility Task
//    c. Stroop Task (Attention)
// 3. These tasks prevent rehearsal of learned pairs
// 4. After 15 minutes → automatically launch Delayed Recall
// 5. Do NOT allow participant to skip or pause

// Transition Message:
// Adolescents: "Great job looking at all the pairs! Before we test 
//   your memory, you'll play a few short brain-teaser games..."
// Adults: "Well done—you've completed the first part. Next, you'll do 
//   a few short thinking activities designed to keep your mind active..."

Filler Task Integration:

  • Import existing RIT component
  • Import existing CFT component
  • Import existing Stroop component (to be developed)
  • Chain them sequentially
  • Track total elapsed time
  • Trigger Delayed Recall at exactly 15:00 minutes

Phase 4: Delayed Cued Recall

// DELAYED RECALL RULES:
// 1. Identical to Immediate Recall in format
// 2. Show OPPOSITE cue from immediate recall
//    - If immediate showed SECOND → now show FIRST
//    - If immediate showed FIRST → now show SECOND
// 3. Same 15-second response window
// 4. Same text input validation
// 5. "Time is up!" message on timeout
// 6. NO feedback on correctness

// Purpose: Measure retention and forgetting over time
// Compare performance: Delayed vs Immediate

Adolescent Delayed Recall Cues (EXACT ORDER):

[
  { cue: 'Pen', correctAnswer: 'Kite' },
  { cue: 'Spoon', correctAnswer: 'Butterfly' },
  { cue: 'Elephant', correctAnswer: 'Mango' },
  { cue: 'Lock', correctAnswer: 'Flute' },
  { cue: 'Cow', correctAnswer: 'Notebook' },
  { cue: 'Cup', correctAnswer: 'Eraser' },
  { cue: 'Tomato', correctAnswer: 'Bag' },
  { cue: 'Scissors', correctAnswer: 'Heart' },
  { cue: 'Goat', correctAnswer: 'Drum' },
  { cue: 'Phone', correctAnswer: 'Auto' },
  { cue: 'Table', correctAnswer: 'Parrot' },
  { cue: 'Chair', correctAnswer: 'Robot' },
  { cue: 'Ship', correctAnswer: 'Potato' },
  { cue: 'Pencil', correctAnswer: 'Dice' },
  { cue: 'Lemon', correctAnswer: 'Belt' },
  { cue: 'Hibiscus', correctAnswer: 'Bike' },
  { cue: 'Carrot', correctAnswer: 'Peacock' },
  { cue: 'Candle', correctAnswer: 'Balloon' },
  { cue: 'Fork', correctAnswer: 'Bulb' },
  { cue: 'Lotus', correctAnswer: 'Sharpener' },
  { cue: 'Shirt', correctAnswer: 'Banana' },
  { cue: 'Chain', correctAnswer: 'Jug' },
  { cue: 'Hand', correctAnswer: 'Rat' },
  { cue: 'Television', correctAnswer: 'Coconut' },
  { cue: 'Book', correctAnswer: 'Key' },
  { cue: 'Shoe', correctAnswer: 'Ears' },
  { cue: 'Crown', correctAnswer: 'Dog' },
  { cue: 'Cloud', correctAnswer: 'Mirror' },
  { cue: 'Cycle', correctAnswer: 'Plate' },
  { cue: 'Pig', correctAnswer: 'Clock' },
  { cue: 'Watch', correctAnswer: 'Crayon' },
  { cue: 'Leaf', correctAnswer: 'Pant' },
  { cue: 'Door', correctAnswer: 'Apple' },
  { cue: 'Hat', correctAnswer: 'Ring' },
  { cue: 'Bucket', correctAnswer: 'Monkey' }
]

Adult Delayed Recall Cues (EXACT ORDER):

[
  { cue: 'Mixer', correctAnswer: 'Spectacles' },
  { cue: 'Mobile Phone', correctAnswer: 'Water Tap' },
  { cue: 'Socks', correctAnswer: 'Lemon' },
  { cue: 'Lamp', correctAnswer: 'Plane' },
  { cue: 'Pillow', correctAnswer: 'Mug' },
  { cue: 'Bowl', correctAnswer: 'Switch' },
  { cue: 'Helmet', correctAnswer: 'Rose' },
  { cue: 'Fan', correctAnswer: 'Shark' },
  { cue: 'Sun', correctAnswer: 'Table' },
  { cue: 'Rock', correctAnswer: 'Peas' },
  { cue: 'Chilli', correctAnswer: 'Hen' },
  { cue: 'Window', correctAnswer: 'Orange' },
  { cue: 'Hammer', correctAnswer: 'Purse' },
  { cue: 'Almonds', correctAnswer: 'Zebra' },
  { cue: 'Ball', correctAnswer: 'Nose' },
  { cue: 'Rope', correctAnswer: 'Ice Cream' },
  { cue: 'Horse', correctAnswer: 'Umbrella' },
  { cue: 'Nose', correctAnswer: 'Cauliflower' },
  { cue: 'Pen', correctAnswer: 'Chair' },
  { cue: 'Clock', correctAnswer: 'Dustbin' },
  { cue: 'Laptop', correctAnswer: 'Moon' },
  { cue: 'Crocodile', correctAnswer: 'Brush' },
  { cue: 'Cup', correctAnswer: 'Rainbow' },
  { cue: 'Scooter', correctAnswer: 'Calendar' },
  { cue: 'Key', correctAnswer: 'Camel' },
  { cue: 'Bank', correctAnswer: 'Watermelon' },
  { cue: 'Jug', correctAnswer: 'Beetle' },
  { cue: 'Iron Box', correctAnswer: 'Strawberry' },
  { cue: 'Sunflower', correctAnswer: 'Ladder' },
  { cue: 'Screwdriver', correctAnswer: 'Garlic' },
  { cue: 'Basket', correctAnswer: 'Train' },
  { cue: 'Camera', correctAnswer: 'Starfish' },
  { cue: 'Candle', correctAnswer: 'Lady\'s Finger' },
  { cue: 'Torch Light', correctAnswer: 'Mat' },
  { cue: 'Cherry', correctAnswer: 'Mirror' }
]

Phase 5: Recognition Phase

// RECOGNITION RULES:
// 1. Show cue image at top in white box
// 2. Display THREE options horizontally below:
//    a. Correct target (the actual paired item)
//    b. Related distractor (same category as target)
//    c. Unrelated distractor (different category)
// 3. RANDOMIZE option positions each trial
// 4. 4-second response window (FAST!)
// 5. Click/tap to select
// 6. Immediate advance on selection
// 7. "Too Late" message for 1 second on timeout
// 8. NO feedback on correctness

// Distractor Selection Logic:
// - Related: Same semantic category (animal→animal, food→food)
// - Unrelated: Completely different category
// - Balance distractor difficulty across trials

Adolescent Recognition Trials (EXACT ORDER with distractors):

[
  { 
    cue: 'Pen', 
    correctTarget: 'Kite', 
    relatedDistractor: 'Parachute', 
    unrelatedDistractor: 'Elephant' 
  },
  { 
    cue: 'Butterfly', 
    correctTarget: 'Spoon', 
    relatedDistractor: 'Knife', 
    unrelatedDistractor: 'Scooter' 
  },
  { 
    cue: 'Mango', 
    correctTarget: 'Elephant', 
    relatedDistractor: 'Cow', 
    unrelatedDistractor: 'Phone' 
  },
  // ... (all 35 trials with distractors from PDF)
]

Adult Recognition Trials (EXACT ORDER with distractors):

[
  { 
    cue: 'Mixer', 
    correctTarget: 'Spectacles', 
    relatedDistractor: 'Sunglasses', 
    unrelatedDistractor: 'Apple' 
  },
  { 
    cue: 'Mobile Phone', 
    correctTarget: 'Water Tap', 
    relatedDistractor: 'Shower', 
    unrelatedDistractor: 'Cow' 
  },
  { 
    cue: 'Lemon', 
    correctTarget: 'Socks', 
    relatedDistractor: 'Shoe', 
    unrelatedDistractor: 'Mango' 
  },
  // ... (all 35 trials with distractors from PDF)
]

📱 Screen Flow (Sequential Development)

Development Order (ONE SCREEN AT A TIME)

Screen 1: IntroScreen

Purpose: Welcome participants and select age group

Components needed:

  • Welcome header with test title
  • Test overview description
  • Age group selector (14-18 vs 18-22)
  • "Let's Go!" button to start

Required files:

  • components/screens/VPAM/DelayedRecallInstructionScreen.jsx

Instructions:

Adolescents:

"Welcome back to the memory game you played earlier! Remember, you saw 
pairs of items and tried to learn which ones go together. Now, again, one 
item from each pair will appear alone. Your task is to write the item that 
goes with it. You'll have a short time for each one, so try to answer quickly. 
If you don't respond in time, the game will say 'Time is up!' and move on 
to the next item.

Write the exact item that goes with the one you see. Don't just write a 
general category, color, or size of the object — try to remember the 
specific pair.

Example: If you see 🍎 Apple and it was paired with 🪑 Chair, write 'Chair', 
not 'furniture' or 'brown chair' or 'big chair'."

Adults:

"We're returning to the memory task you did before. Earlier, you learned 
pairs of items and tried to remember which ones belonged together. In this 
part, again, a single item from each pair will appear alone. Your task is 
to type or write the item that was originally paired with it. You will have 
limited time to respond for each item. If you do not answer within the time 
limit, the message 'Time is up!' will appear and the task will move on.

Type the exact item that was paired with the cue you see. Do not write a 
general category, color, or size of the object — focus on the specific 
paired item.

Example: If the cue is 🖊️ Pen and it was paired with 🐘 Elephant, type 
'Elephant', not 'animal', 'creature', or 'grey elephant' or 'big elephant'."

Screen 8: DelayedRecallScreen

Purpose: Test memory retention after 15-minute delay

Components needed:

  • Same as ImmediateRecallScreen
  • Reuse RecallInput component
  • Different cue sequence

Required files:

  • components/screens/VPAM/DelayedRecallScreen.jsx
  • Reuse: components/shared/VPAM/RecallInput.jsx
  • Extend: hooks/vpam/useRecallScreen.js

Logic:

  • Identical to Immediate Recall
  • Use OPPOSITE cue from immediate recall
  • Same 15-second response window
  • Same validation rules
  • Track all same metrics
  • After trial 35 → transition to Recognition

Screen 9: RecognitionInstructionScreen

Purpose: Explain multiple choice recognition task

Components needed:

  • Instruction text
  • Visual example of 3-option layout
  • Speed reminder (4 seconds!)
  • "Let's Go!" button

Required files:

  • components/screens/VPAM/RecognitionInstructionScreen.jsx

Instructions:

Adolescents:

"Next, you'll see one picture at the top of the screen. Below it, you'll 
see three choices. Your job is to pick the picture that was originally 
paired with the one on top. Be quick—you'll only have a few seconds for 
each choice. If you don't tap the suitable choice on time, the game will 
say 'Too Late!' and move on."

Adults:

"In this final part, you'll again see one item at the top of the screen. 
Below it, three choices will be shown. Select the item that was originally 
paired with the one on top. Be ready—each trial only lasts a few seconds. 
If you don't tap the suitable choice on time, the game will say 'Too Late!' 
and move on."

Screen 10: RecognitionScreen

Purpose: Multiple choice recognition test

Components needed:

  • Cue image display (top, white box)
  • Three option buttons (horizontal layout)
  • Click/tap detection
  • Timer (4 seconds)
  • "Too Late" message overlay
  • Trial counter

Required files:

  • components/screens/VPAM/RecognitionScreen.jsx
  • components/shared/VPAM/RecognitionOptions.jsx
  • hooks/vpam/useRecognitionScreen.js

Critical logic:

// For each trial:
1. Display cue image at top
2. Randomize 3 option positions:
   - Correct target
   - Related distractor
   - Unrelated distractor
3. Start 4-second timer
4. Listen for option selection
5. On selection:
   - Highlight selected option briefly
   - Record choice and RT
   - Validate accuracy
   - Advance immediately
6. On timeout:
   - Show "Too Late" for 1 second
   - Record as no response (accuracy = 0)
   - Advance to next trial
7. After trial 35  transition to Results

Option randomization:

function randomizeOptions(correct, related, unrelated) {
  const options = [
    { id: 'correct', image: correct, isCorrect: true },
    { id: 'related', image: related, isCorrect: false },
    { id: 'unrelated', image: unrelated, isCorrect: false }
  ];
  
  // Fisher-Yates shuffle
  for (let i = options.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [options[i], options[j]] = [options[j], options[i]];
  }
  
  return options;
}

Screen 11: ResultsScreen

Purpose: Display performance metrics and export data

Components needed:

  • Performance summary cards
  • Accuracy breakdown by phase
  • Reaction time statistics
  • Consolidation slope visualization
  • Data export buttons (JSON, CSV)
  • "Finish" or "Return to Test Selection" button

Required files:

  • components/screens/VPAM/ResultsScreen.jsx
  • components/shared/VPAM/MetricsCard.jsx
  • components/shared/VPAM/PhaseComparison.jsx
  • utils/vpamCalculations.js
  • utils/vpamDataExport.js

Metrics to display:

// Accuracy Metrics (%)
1. Immediate Recall Accuracy
2. Delayed Recall Accuracy
3. Recognition Accuracy
4. Overall Task Accuracy

// Response Metrics
5. Total Responses Given
6. Total No Responses (timeouts)
7. Correct Responses (all phases)
8. Incorrect Responses (all phases)

// Reaction Time (ms)
9. Mean RT - Immediate Recall
10. Mean RT - Delayed Recall
11. Mean RT - Recognition
12. Overall Mean RT

// Memory Change
13. Consolidation Slope (Absolute)
    = Delayed Accuracy - Immediate Accuracy
14. Consolidation Slope (%)
    = (Delayed - Immediate) / Immediate × 100

Completion messages:

Adolescents:

"That's it! You've finished this memory game. Great work!"

Adults:

"This concludes the memory task. Thank you for your participation."

🎨 Design Requirements

Visual Design Principles

  • Clean, focused interface - minimize cognitive load
  • Age-appropriate aesthetics:
    • Adolescents: Colorful gradient background, playful fonts
    • Adults: Soft neutral background, professional fonts
  • Responsive: 345px to large screens
  • Accessibility: High contrast, clear typography
  • Minimal animations - don't distract from memory task

Technology Stack

  • Framework: React with JavaScript (JSX)
  • Styling: Tailwind CSS only (no custom CSS files)
  • Animations: Framer Motion (minimal, subtle only)
  • State Management: React Hooks (useState, useEffect, useRef)
  • NO localStorage/sessionStorage - use in-memory state only
  • NO external image hosting - images in public/images/vpam/

Image Requirements

Image specifications:

Location: public/images/vpam/
Format: PNG or SVG
Size: ~200x200px (consistent dimensions)
Quality: High-res but optimized (<50KB each)
Background: Transparent or white
Naming: lowercase, hyphens (e.g., magic-potion.png)

Total images needed:
- Adolescents: 70 unique images (35 pairs × 2)
- Adults: 70 unique images (35 pairs × 2)
- Distractors: ~70 additional images for recognition

Total: ~210 images

Image organization:

public/images/vpam/
├── adolescent/
│   ├── pen.png
│   ├── kite.png
│   ├── butterfly.png
│   └── ... (70 items)
├── adult/
│   ├── mixer.png
│   ├── spectacles.png
│   ├── mobile-phone.png
│   └── ... (70 items)
└── distractors/
    ├── parachute.png
    ├── knife.png
    └── ... (70 items)

Stimulus Display Specifications

Encoding Phase:

// Display format:
<div className="bg-white border-4 border-gray-300 rounded-lg p-8 
                flex items-center justify-center gap-8">
  <img src={firstImage} className="w-40 h-40 object-contain" />
  <img src={secondImage} className="w-40 h-40 object-contain" />
</div>

// Or stacked for mobile:
<div className="flex flex-col gap-4">
  <img src={firstImage} className="w-32 h-32 object-contain" />
  <img src={secondImage} className="w-32 h-32 object-contain" />
</div>

Recall Phase:

// Cue display:
<div className="bg-white border-4 border-gray-300 rounded-lg p-8 
                flex items-center justify-center">
  <img src={cueImage} className="w-48 h-48 object-contain" />
</div>

// Input field:
<input 
  type="text"
  className="w-full max-w-md px-4 py-3 text-lg border-2 
             border-gray-400 rounded-lg focus:border-blue-500 
             focus:ring-2 focus:ring-blue-200"
  placeholder="Type the paired item..."
  maxLength="50"
/>

Recognition Phase:

// Cue at top:
<div className="mb-8">
  <img src={cueImage} className="w-32 h-32 object-contain mx-auto" />
</div>

// Three options (horizontal):
<div className="flex flex-wrap justify-center gap-4">
  {options.map(option => (
    <button 
      className="border-4 border-gray-300 rounded-lg p-4 
                 hover:border-blue-400 active:bg-blue-50 
                 transition-colors"
      onClick={() => handleSelection(option)}
    >
      <img src={option.image} className="w-32 h-32 object-contain" />
    </button>
  ))}
</div>

// Selected state:
<button className="border-4 border-blue-500 bg-blue-50 ring-4 ring-blue-200">
  <img src={selectedOption.image} />
</button>

📊 Data Recording Requirements

Trial Data Structure

Every trial must record:

{
  // Identification
  trialNumber: number, // 1-105 (35 encoding + 35 immediate + 35 delayed + 35 recognition)
  phase: 'encoding' | 'immediate_recall' | 'delayed_recall' | 'recognition',
  trialInPhase: number, // 1-35 per phase
  
  // Stimulus information
  cueItem: string, // 'Kite', 'Spectacles', etc.
  targetItem: string, // 'Pen', 'Mixer', etc.
  distractorRelated: string | null, // 'Parachute' (recognition only)
  distractorUnrelated: string | null, // 'Elephant' (recognition only)
  
  // Response data
  participantResponse: string | null, // Typed text or selected option
  responseTime: number | null, // milliseconds from stimulus onset
  responseAccuracy: 0 | 1, // 1 = correct, 0 = incorrect/timeout
  responseType: 'typed' | 'selected' | 'timeout' | 'no-response',
  
  // Recognition specific
  selectedOption: 'correct' | 'related' | 'unrelated' | null,
  optionPositions: array | null, // [0, 1, 2] randomized order
  
  // Timing
  stimulusOnsetTime: number, // timestamp
  responseSubmitTime: number | null, // timestamp
  encodingDuration: number | null, // encoding phase only
  
  // Metadata
  ageGroup: 'adolescent' | 'adult',
  timestamp: number // Date.now()
}

Summary Data Structure

After test completion:

{
  // Participant info
  participantInfo: {
    ageGroup: 'adolescent' | 'adult',
    testDate: string,
    testStartTime: string,
    testEndTime: string,
    totalDuration: number, // minutes
    delayDurationActual: number // should be ~15 minutes
  },
  
  // Accuracy metrics (%)
  accuracy: {
    immediateRecall: number,
    delayedRecall: number,
    recognition: number,
    overall: number
  },
  
  // Response counts
  responseCounts: {
    totalTrials: 105,
    totalResponses: number,
    totalTimeouts: number,
    correctResponses: number,
    incorrectResponses: number,
    
    // By phase
    immediateRecallAnswered: number,
    delayedRecallAnswered: number,
    recognitionAnswered: number
  },
  
  // Reaction time statistics (ms)
  reactionTimes: {
    immediateRecallMean: number,
    delayedRecallMean: number,
    recognitionMean: number,
    overallMean: number,
    
    // Additional stats
    immediateRecallSD: number,
    delayedRecallSD: number,
    recognitionSD: number
  },
  
  // Memory consolidation
  consolidation: {
    absoluteChange: number, // Delayed - Immediate
    percentageChange: number, // (Delayed - Immediate) / Immediate × 100
    forgettingRate: number, // Items forgotten
    itemsRetained: number // Correct in both phases
  },
  
  // Phase-specific accuracy rates
  phaseAccuracy: {
    immediateRecall: {
      correct: number,
      incorrect: number,
      timeout: number,
      accuracy: number // %
    },
    delayedRecall: {
      correct: number,
      incorrect: number,
      timeout: number,
      accuracy: number // %
    },
    recognition: {
      correct: number,
      relatedDistractorChosen: number,
      unrelatedDistractorChosen: number,
      timeout: number,
      accuracy: number // %
    }
  },
  
  // Raw trial data
  trialData: [/* array of all trial records */]
}

Data Export Formats

JSON Export:

// Filename: vpam_results_[ageGroup]_[timestamp].json
// Full data structure with all nested objects

CSV Export:

// Filename: vpam_results_[ageGroup]_[timestamp].csv
// Flattened trial data with column headers:
// trial_number, phase, trial_in_phase, cue_item, target_item,
// participant_response, response_time, accuracy, ...

🔧 Key Constants to Define

vpamConfig.js

export const VPAM_CONFIG = {
  TOTAL_PAIRS: 35,
  PHASES: ['encoding', 'immediate_recall', 'delayed_recall', 'recognition'],
  
  TIMING: {
    // Encoding
    ENCODING_DURATION_ADOLESCENT: 3500, // ms
    ENCODING_DURATION_ADULT: 2500, // ms
    INTER_PAIR_INTERVAL: 300, // ms blank screen between pairs
    
    // Recall phases
    RECALL_RESPONSE_WINDOW: 15000, // ms (both age groups)
    TIMEOUT_MESSAGE_DURATION: 1000, // ms
    
    // Recognition phase
    RECOGNITION_RESPONSE_WINDOW: 4000, // ms (both age groups)
    TOO_LATE_MESSAGE_DURATION: 1000, // ms
    
    // Delay interval
    DELAY_DURATION: 900000, // ms (15 minutes)
  },
  
  FILLER_TASKS: [
    'response_inhibition',
    'cognitive_flexibility',
    'stroop' // if available
  ],
  
  VALIDATION: {
    MAX_INPUT_LENGTH: 50,
    CASE_SENSITIVE: false,
    TRIM_WHITESPACE: true,
    ALLOW_TYPOS: false // set to true for lenient matching
  }
};

vpamInstructions.js

export const VPAM_INSTRUCTIONS = {
  adolescent: {
    intro: {
      title: "Memory Game: Picture Pairs",
      description: `In this game, you will see pictures of everyday things. 
        Some of these pictures will be shown together as pairs. Your task is 
        to pay close attention and try to remember which ones are being 
        presented as pairs, because later we will test your memory in different 
        ways. Sometimes you will have to write the missing item, and sometimes 
        you will choose it from a few options. Let's get started!`,
      buttonText: "Let's Go!"
    },
    
    encoding: {
      title: "Learning Phase",
      description: `You will see two pictures appear inside a box on the screen. 
        Just look carefully and try to remember which pictures are being 
        presented as pairs. You don't need to press anything. Each pair will 
        disappear after a few seconds, so focus while it is on the screen.`,
      buttonText: "Let's Go!"
    },
    
    immediateRecall: {
      title: "Memory Test: Part 1",
      description: `Great job learning the pairs! Now, one item from each pair 
        will appear by itself. Your task is to write the item that goes with it. 
        Try to answer quickly — you'll have a short time for each one.`,
      reminder: `Remember: write the exact item that was paired. Don't just 
        write a general category, color, or size — focus on the specific item.`,
      example: `Example: If you see 🍎 Apple and it was paired with 🪑 Chair, 
        write 'Chair', not 'furniture' or 'brown chair' or 'big chair'.`,
      buttonText: "Let's Go!"
    },
    
    delayTransition: {
      title: "Brain Break!",
      description: `Great job looking at all the pairs! Before we test your 
        memory, you'll play a few short brain-teaser games. These games are 
        like a quick workout for your mind. Once you're done, we'll bring you 
        back to check how many pairs you can remember!`,
      buttonText: "Let's Go!"
    },
    
    delayedRecall: {
      title: "Memory Test: Part 2",
      description: `Welcome back to the memory game you played earlier! Remember, 
        you saw pairs of items and tried to learn which ones go together. Now, 
        again, one item from each pair will appear alone. Your task is to write 
        the item that goes with it. You'll have a short time for each one, so 
        try to answer quickly.`,
      reminder: `Write the exact item that goes with the one you see. Don't just 
        write a general category, color, or size of the object — try to remember 
        the specific pair.`,
      buttonText: "Let's Go!"
    },
    
    recognition: {
      title: "Memory Test: Final Part",
      description: `Next, you'll see one picture at the top of the screen. Below 
        it, you'll see three choices. Your job is to pick the picture that was 
        originally paired with the one on top. Be quick—you'll only have a few 
        seconds for each choice.`,
      buttonText: "Let's Go!"
    },
    
    completion: {
      title: "Awesome Work!",
      message: "That's it! You've finished this memory game. Great work!",
      buttonText: "View Results"
    }
  },
  
  adult: {
    intro: {
      title: "Visual Paired Associates Memory Test",
      description: `In this task, you will see images of everyday objects. 
        Some of these images will be presented together as pairs. Your task is 
        to observe carefully and remember which items are paired together, as 
        your memory will be tested in different ways. You will sometimes need 
        to type the missing item, and other times select it from multiple options.`,
      buttonText: "Begin Task"
    },
    
    encoding: {
      title: "Learning Phase",
      description: `You will see two items presented together inside a box. Your 
        task is to observe carefully and remember which items are being presented 
        as pairs. You don't need to press anything. Each pair will only remain on 
        screen briefly, so focus while it is shown.`,
      buttonText: "Start Learning"
    },
    
    immediateRecall: {
      title: "Immediate Recall Test",
      description: `You've just seen all the pairs. Now, a single item from each 
        pair will appear alone. Your task is to type or write the item that was 
        originally paired with it. Respond quickly — each trial has a time limit.`,
      reminder: `Type the exact item that was paired. Do not write a general 
        category, color, or size — focus on the specific item.`,
      example: `Example: If the cue is 🖊️ Pen and it was paired with 🐘 Elephant, 
        type 'Elephant', not 'animal', 'creature', or 'grey elephant'.`,
      buttonText: "Begin Recall"
    },
    
    delayTransition: {
      title: "Interim Activities",
      description: `Well done—you've completed the first part. Next, you'll do a 
        few short thinking activities designed to keep your mind active while your 
        memory continues to work in the background. After that, we'll return to 
        test how well you remember the pairs.`,
      buttonText: "Continue"
    },
    
    delayedRecall: {
      title: "Delayed Recall Test",
      description: `We're returning to the memory task you did before. Earlier, 
        you learned pairs of items and tried to remember which ones belonged 
        together. In this part, again, a single item from each pair will appear 
        alone. Your task is to type or write the item that was originally paired 
        with it.`,
      reminder: `Type the exact item that was paired with the cue you see. Do not 
        write a general category, color, or size of the object — focus on the 
        specific paired item.`,
      buttonText: "Begin Test"
    },
    
    recognition: {
      title: "Recognition Test",
      description: `In this final part, you'll again see one item at the top of 
        the screen. Below it, three choices will be shown. Select the item that 
        was originally paired with the one on top. Be ready—each trial only lasts 
        a few seconds.`,
      buttonText: "Begin Recognition"
    },
    
    completion: {
      title: "Task Complete",
      message: "This concludes the memory task. Thank you for your participation.",
      buttonText: "View Results"
    }
  }
};

vpamPairs.js

// EXACT sequences from PDF
export const VPAM_PAIRS = {
  adolescent: [
    { id: 1, first: 'Pen', second: 'Kite', firstImg: 'pen.png', secondImg: 'kite.png' },
    { id: 2, first: 'Butterfly', second: 'Spoon', firstImg: 'butterfly.png', secondImg: 'spoon.png' },
    // ... all 35 pairs
  ],
  
  adult: [
    { id: 1, first: 'Mixer', second: 'Spectacles', firstImg: 'mixer.png', secondImg: 'spectacles.png' },
    { id: 2, first: 'Mobile Phone', second: 'Water Tap', firstImg: 'mobile-phone.png', secondImg: 'water-tap.png' },
    // ... all 35 pairs
  ]
};

// Immediate recall cues (show SECOND, ask for FIRST)
export const IMMEDIATE_RECALL_CUES = {
  adolescent: [
    { id: 1, cue: 'Kite', cueImg: 'kite.png', correctAnswer: 'Pen', correctImg: 'pen.png' },
    { id: 2, cue: 'Butterfly', cueImg: 'butterfly.png', correctAnswer: 'Spoon', correctImg: 'spoon.png' },
    // ... all 35 trials
  ],
  
  adult: [
    { id: 1, cue: 'Spectacles', cueImg: 'spectacles.png', correctAnswer: 'Mixer', correctImg: 'mixer.png' },
    // ... all 35 trials
  ]
};

// Delayed recall cues (show FIRST, ask for SECOND)
export const DELAYED_RECALL_CUES = {
  adolescent: [
    { id: 1, cue: 'Pen', cueImg: 'pen.png', correctAnswer: 'Kite', correctImg: 'kite.png' },
    // ... all 35 trials
  ],
  
  adult: [
    { id: 1, cue: 'Mixer', cueImg: 'mixer.png', correctAnswer: 'Spectacles', correctImg: 'spectacles.png' },
    // ... all 35 trials
  ]
};

// Recognition trials with distractors
export const RECOGNITION_TRIALS = {
  adolescent: [
    {
      id: 1,
      cue: 'Pen',
      cueImg: 'pen.png',
      correctTarget: 'Kite',
      correctImg: 'kite.png',
      relatedDistractor: 'Parachute',
      relatedImg: 'parachute.png',
      unrelatedDistractor: 'Elephant',
      unrelatedImg: 'elephant.png'
    },
    // ... all 35 trials with distractors
  ],
  
  adult: [
    {
      id: 1,
      cue: 'Mixer',
      cueImg: 'mixer.png',
      correctTarget: 'Spectacles',
      correctImg: 'spectacles.png',
      relatedDistractor: 'Sunglasses',
      relatedImg: 'sunglasses.png',
      unrelatedDistractor: 'Apple',
      unrelatedImg: 'apple.png'
    },
    // ... all 35 trials
  ]
};

📁 File Structure Requirements

src/
├── components/
│   ├── screens/
│   │   ├── VPAM/
│   │   │   ├── IntroScreen.jsx
│   │   │   ├── EncodingInstructionScreen.jsx
│   │   │   ├── EncodingScreen.jsx
│   │   │   ├── ImmediateRecallInstructionScreen.jsx
│   │   │   ├── ImmediateRecallScreen.jsx
│   │   │   ├── DelayIntervalScreen.jsx
│   │   │   ├── DelayedRecallInstructionScreen.jsx
│   │   │   ├── DelayedRecallScreen.jsx
│   │   │   ├── RecognitionInstructionScreen.jsx
│   │   │   ├── RecognitionScreen.jsx
│   │   │   ├── ResultsScreen.jsx
│   │   │   └── VisualPairedAssociatesTest.jsx (main orchestrator)
│   │   │
│   │   ├── CFT/           # Existing
│   │   ├── RIT/           # Existing
│   │   └── TestSelectionScreen.jsx
│   │
│   └── shared/
│       └── VPAM/
│           ├── PairDisplay.jsx
│           ├── RecallInput.jsx
│           ├── RecognitionOptions.jsx
│           ├── TrialCounter.jsx
│           ├── Timer.jsx
│           ├── MetricsCard.jsx
│           └── PhaseComparison.jsx
│
├── hooks/
│   └── vpam/
│       ├── useEncodingScreen.js
│       ├── useRecallScreen.js
│       ├── useRecognitionScreen.js
│       └── useDelayInterval.js
│
├── utils/
│   ├── vpamTimer.js
│   ├── vpamValidation.js
│   ├── vpamCalculations.js
│   ├── vpamDataExport.js
│   └── vpamAnimations.js
│
├── constants/
│   ├── vpamConfig.js
│   ├── vpamInstructions.js
│   ├── vpamPairs.js
│   └── testsConfig.js (update to include VPAM)
│
└── public/
    └── images/
        └── vpam/
            ├── adolescent/
            │   ├── pen.png
            │   ├── kite.png
            │   └── ... (70 images)
            ├── adult/
            │   ├── mixer.png
            │   ├── spectacles.png
            │   └── ... (70 images)
            └── distractors/
                ├── parachute.png
                ├── knife.png
                └── ... (70 images)

🚫 Important Constraints

NEVER Do This

  1. Change pair order (must follow PDF exactly)
  2. Show feedback on correctness during test
  3. Allow skipping trials
  4. Let participants replay encoding phase
  5. Accept category answers (require specific items)
  6. Use emojis as actual stimuli (use real images)
  7. Skip or abbreviate delay interval
  8. Randomize recall cue order (follow PDF)
  9. Show scores during test (only at end)
  10. Use localStorage or sessionStorage

ALWAYS Do This

  1. Follow exact pair sequences from PDF
  2. Use age-specific timing (3.5s vs 2.5s encoding)
  3. Show "Time is up!" / "Too Late" on timeout
  4. Validate exact item names only
  5. Randomize recognition option positions
  6. Track all metrics silently during test
  7. Implement 15-minute delay with filler tasks
  8. Use opposite cues for delayed vs immediate recall
  9. Display clear, high-contrast images
  10. Export complete data at end

🔧 Development Workflow

Step-by-Step Process

  1. Current Phase: Developer specifies which screen to create
  2. AI Creates: Complete screen with all required files
  3. AI Shows: Code for that screen only
  4. AI Asks: "Screen [Name] complete. Proceed to [NextScreen]?"
  5. Developer: Approves or requests changes
  6. Repeat: Until all 11 screens are complete
  7. Integration: Connect VPAM to TestSelectionScreen
  8. Testing: Verify timing, data collection, export

Code Quality Standards

  • Use functional components with hooks
  • Separate concerns (logic in hooks, UI in components)
  • Add JSDoc comments for complex functions
  • Follow consistent naming conventions (camelCase)
  • Use Tailwind for ALL styling (no inline styles)
  • Handle edge cases (timeout, rapid input, empty responses)
  • Create reusable components (don't duplicate code)
  • Optimize re-renders with useMemo/useCallback
  • Implement proper cleanup in useEffect

State Management Pattern

// Main VPAM Test State (Context or prop drilling)
const [testState, setTestState] = useState({
  currentScreen: 'intro', // 'intro', 'encoding', 'immediateRecall', etc.
  ageGroup: null, // 'adolescent' or 'adult'
  phase: null, // 'encoding', 'immediate_recall', 'delayed_recall', 'recognition'
  currentTrial: 0, // 0-34 per phase
  trialData: [], // All recorded trials
  startTime: null,
  delayStartTime: null,
  delayEndTime: null
});

// Per-Screen Local State
const [screenState, setScreenState] = useState({
  showStimulus: false,
  currentPair: null,
  userResponse: '',
  isSubmitting: false,
  showTimeout: false,
  reactionStartTime: null
});

🎭 Animation Specifications

Framer Motion Animations (utils/vpamAnimations.js)

// Minimal, non-distracting animations

export const fadeIn = {
  initial: { opacity: 0 },
  animate: { opacity: 1 },
  transition: { duration: 0.3 }
};

export const slideUp = {
  initial: { y: 20, opacity: 0 },
  animate: { y: 0, opacity: 1 },
  transition: { duration: 0.4, ease: 'easeOut' }
};

export const pairTransition = {
  initial: { opacity: 0, scale: 0.95 },
  animate: { opacity: 1, scale: 1 },
  exit: { opacity: 0, scale: 0.95 },
  transition: { duration: 0.2 }
};

export const timeoutPulse = {
  initial: { scale: 1 },
  animate: { 
    scale: [1, 1.1, 1],
    opacity: [1, 0.8, 0]
  },
  transition: { duration: 1 }
};

// NO complex animations during encoding/recall
// Keep cognitive load minimal

🎯 Timing Flow (CRITICAL)

Encoding Phase Timing

// For each of 35 pairs:

1. Display pair immediately (no fade-in delay)
   - Both images visible simultaneously
   - Centered in white box

2. Hold for exposure duration
   - Adolescent: 3.5 seconds
   - Adult: 2.5 seconds
   - No user interaction

3. Brief blank screen (0.3 seconds)
   - Inter-pair interval
   - Prevents visual overlap

4. Next pair appears
   - Repeat steps 1-3

5. After pair 35  transition to Immediate Recall
   - No delay, automatic progression

// Total encoding time:
// Adolescents: (3.5s + 0.3s) × 35 = 133 seconds (~2.2 min)
// Adults: (2.5s + 0.3s) × 35 = 98 seconds (~1.6 min)

Recall Phase Timing

// For each of 35 trials:

1. Display cue image immediately
   - Start reaction timer
   - Show text input field (empty)

2. Wait for response OR timeout (15 seconds)
   - Listen for:
     * Text input changes
     * Submit button click
     * Enter key press
     * Timer expiration

3. On Submit (before timeout):
   - Record response text
   - Calculate reaction time
   - Validate accuracy
   - Clear input field
   - Proceed to next trial immediately

4. On Timeout (no submit):
   - Show "Time is up!" (1 second)
   - Record as no response (accuracy = 0)
   - Clear input field
   - Proceed to next trial

5. Brief blank screen (optional, 0.3s)
   - Or immediately show next cue

// Total recall time (worst case - all timeouts):
// (15s + 1s) × 35 = 560 seconds (~9.3 min per phase)

Recognition Phase Timing

// For each of 35 trials:

1. Display cue at top
2. Display 3 randomized options below
3. Start 4-second timer
4. Wait for selection OR timeout

5. On Selection (before timeout):
   - Highlight selected option briefly (0.2s)
   - Record choice and RT
   - Validate accuracy
   - Proceed to next trial immediately

6. On Timeout (no selection):
   - Show "Too Late" (1 second)
   - Record as no response (accuracy = 0)
   - Proceed to next trial

// Total recognition time (worst case):
// (4s + 1s) × 35 = 175 seconds (~2.9 min)

Delay Interval Timing

// 15-minute delay with filler tasks

1. Show transition message (5 seconds)
2. Launch Response Inhibition Test (~7 min)
3. Launch Cognitive Flexibility Test (~6 min)
4. Launch Stroop Test (~2 min) [if available]
5. Auto-trigger Delayed Recall at 15:00

// Critical: Track elapsed time precisely
// Use Date.now() or performance.now()
// Don't rely on setTimeout for long durations

📋 Scoring Logic (CRITICAL)

Response Validation

/**
 * Validates user response against correct answer
 * @param {string} userInput - Raw user input
 * @param {string} correctAnswer - Expected answer
 * @returns {boolean} - True if correct
 */
function validateResponse(userInput, correctAnswer) {
  // Normalize both strings
  const normalized = userInput
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s]/g, ''); // Remove punctuation
  
  const correct = correctAnswer
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s]/g, '');
  
  // Exact match required
  if (normalized === correct) {
    return true;
  }
  
  // Optional: Handle common variations
  // "lady's finger" vs "ladys finger" vs "ladies finger"
  const variations = generateVariations(correct);
  return variations.includes(normalized);
}

/**
 * Generate acceptable variations for hyphenated/possessive words
 */
function generateVariations(word) {
  return [
    word,
    word.replace(/[\s-']/g, ''), // Remove spaces, hyphens, apostrophes
    word.replace(/'/g, ''), // Remove apostrophes only
    word.replace(/-/g, ' '), // Replace hyphens with spaces
    word.replace(/\s/g, '') // Remove all spaces
  ];
}

Accuracy Calculation

/**
 * Calculate accuracy metrics for each phase
 */
function calculateAccuracy(trialData, phase) {
  const phaseTrials = trialData.filter(t => t.phase === phase);
  const totalTrials = phaseTrials.length;
  const answered = phaseTrials.filter(t => t.responseType !== 'timeout').length;
  const correct = phaseTrials.filter(t => t.responseAccuracy === 1).length;
  
  return {
    totalTrials,
    answered,
    notAnswered: totalTrials - answered,
    correct,
    incorrect: answered - correct,
    accuracyRate: totalTrials > 0 ? (correct / totalTrials) * 100 : 0,
    answeredAccuracyRate: answered > 0 ? (correct / answered) * 100 : 0
  };
}

/**
 * Calculate overall task metrics
 */
function calculateOverallMetrics(trialData) {
  const immediateRecall = calculateAccuracy(trialData, 'immediate_recall');
  const delayedRecall = calculateAccuracy(trialData, 'delayed_recall');
  const recognition = calculateAccuracy(trialData, 'recognition');
  
  // Consolidation slope
  const absoluteChange = delayedRecall.accuracyRate - immediateRecall.accuracyRate;
  const percentageChange = immediateRecall.accuracyRate > 0
    ? (absoluteChange / immediateRecall.accuracyRate) * 100
    : 0;
  
  // Overall accuracy
  const totalCorrect = immediateRecall.correct + delayedRecall.correct + recognition.correct;
  const totalTrials = immediateRecall.totalTrials + delayedRecall.totalTrials + recognition.totalTrials;
  const overallAccuracy = totalTrials > 0 ? (totalCorrect / totalTrials) * 100 : 0;
  
  return {
    immediateRecall,
    delayedRecall,
    recognition,
    consolidation: {
      absoluteChange,
      percentageChange,
      forgettingRate: Math.max(0, -absoluteChange), // Only if negative
      itemsRetained: Math.min(immediateRecall.correct, delayedRecall.correct)
    },
    overall: {
      accuracy: overallAccuracy,
      totalCorrect,
      totalTrials,
      totalAnswered: immediateRecall.answered + delayedRecall.answered + recognition.answered
    }
  };
}

Reaction Time Calculation

/**
 * Calculate reaction time statistics
 */
function calculateReactionTimes(trialData, phase) {
  const phaseTrials = trialData
    .filter(t => t.phase === phase && t.responseTime !== null);
  
  if (phaseTrials.length === 0) {
    return { mean: null, median: null, sd: null, min: null, max: null };
  }
  
  const times = phaseTrials.map(t => t.responseTime);
  const mean = times.reduce((a, b) => a + b, 0) / times.length;
  
  // Standard deviation
  const variance = times.reduce((sum, time) => {
    return sum + Math.pow(time - mean, 2);
  }, 0) / times.length;
  const sd = Math.sqrt(variance);
  
  // Median
  const sorted = [...times].sort((a, b) => a - b);
  const median = sorted.length % 2 === 0
    ? (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2
    : sorted[Math.floor(sorted.length / 2)];
  
  return {
    mean: Math.round(mean),
    median: Math.round(median),
    sd: Math.round(sd),
    min: Math.min(...times),
    max: Math.max(...times),
    count: times.length
  };
}

🎨 Component Examples

PairDisplay Component

// components/shared/VPAM/PairDisplay.jsx

import React from 'react';
import { motion } from 'framer-motion';

/**
 * Displays a pair of images during encoding phase
 */
export default function PairDisplay({ firstImage, secondImage, ageGroup }) {
  const containerBg = ageGroup === 'adolescent' 
    ? 'bg-gradient-to-br from-purple-100 to-blue-100' 
    : 'bg-gray-50';
  
  return (
    <motion.div
      initial={{ opacity: 0, scale: 0.95 }}
      animate={{ opacity: 1, scale: 1 }}
      exit={{ opacity: 0, scale: 0.95 }}
      transition={{ duration: 0.2 }}
      className={`${containerBg} min-h-screen flex items-center justify-center p-4`}
    >
      <div className="bg-white border-4 border-gray-300 rounded-2xl p-8 shadow-lg">
        <div className="flex flex-col md:flex-row items-center justify-center gap-8 md:gap-12">
          <img 
            src={firstImage} 
            alt="First item"
            className="w-40 h-40 md:w-48 md:h-48 object-contain"
          />
          <div className="text-4xl text-gray-400 font-bold">+</div>
          <img 
            src={secondImage} 
            alt="Second item"
            className="w-40 h-40 md:w-48 md:h-48 object-contain"
          />
        </div>
      </div>
    </motion.div>
  );
}

RecallInput Component

// components/shared/VPAM/RecallInput.jsx

import React, { useState, useEffect, useRef } from 'react';
import { motion } from 'framer-motion';

/**
 * Input component for recall phases
 */
export default function RecallInput({ 
  cueImage, 
  onSubmit, 
  timeLimit = 15000,
  showTimeout,
  disabled 
}) {
  const [input, setInput] = useState('');
  const [timeRemaining, setTimeRemaining] = useState(timeLimit / 1000);
  const inputRef = useRef(null);
  
  useEffect(() => {
    // Focus input on mount
    inputRef.current?.focus();
  }, [cueImage]);
  
  useEffect(() => {
    // Countdown timer
    const interval = setInterval(() => {
      setTimeRemaining(prev => Math.max(0, prev - 0.1));
    }, 100);
    
    return () => clearInterval(interval);
  }, []);
  
  const handleSubmit = (e) => {
    e.preventDefault();
    if (input.trim() && !disabled) {
      onSubmit(input.trim());
      setInput('');
    }
  };
  
  const progressPercent = (timeRemaining / (timeLimit / 1000)) * 100;
  const isUrgent = timeRemaining < 5;
  
  return (
    <div className="flex flex-col items-center gap-6">
      {/* Cue Image */}
      <div className="bg-white border-4 border-gray-300 rounded-2xl p-6 shadow-lg">
        <img 
          src={cueImage} 
          alt="Cue" 
          className="w-48 h-48 object-contain"
        />
      </div>
      
      {/* Timer Bar */}
      <div className="w-full max-w-md h-2 bg-gray-200 rounded-full overflow-hidden">
        <motion.div
          className={`h-full ${isUrgent ? 'bg-red-500' : 'bg-blue-500'}`}
          initial={{ width: '100%' }}
          animate={{ width: `${progressPercent}%` }}
          transition={{ duration: 0.1 }}
        />
      </div>
      
      {/* Input Form */}
      <form onSubmit={handleSubmit} className="w-full max-w-md">
        <input
          ref={inputRef}
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          disabled={disabled}
          placeholder="Type the paired item..."
          maxLength={50}
          className="w-full px-6 py-4 text-lg border-3 border-gray-400 
                     rounded-xl focus:border-blue-500 focus:ring-4 
                     focus:ring-blue-200 outline-none transition-all
                     disabled:bg-gray-100 disabled:cursor-not-allowed"
        />
        
        <button
          type="submit"
          disabled={!input.trim() || disabled}
          className="w-full mt-4 px-6 py-4 text-lg font-semibold 
                     bg-blue-500 text-white rounded-xl 
                     hover:bg-blue-600 active:scale-95 
                     disabled:bg-gray-300 disabled:cursor-not-allowed 
                     transition-all"
        >
          Submit Answer
        </button>
      </form>
      
      {/* Timeout Message */}
      {showTimeout && (
        <motion.div
          initial={{ scale: 0.8, opacity: 0 }}
          animate={{ scale: 1, opacity: 1 }}
          exit={{ scale: 0.8, opacity: 0 }}
          className="fixed inset-0 flex items-center justify-center 
                     bg-black bg-opacity-50 z-50"
        >
          <div className="bg-white px-12 py-8 rounded-2xl shadow-2xl">
            <p className="text-3xl font-bold text-red-600">Time is up!</p>
          </div>
        </motion.div>
      )}
    </div>
  );
}

RecognitionOptions Component

// components/shared/VPAM/RecognitionOptions.jsx

import React, { useState, useEffect } from 'react';
import { motion } from 'framer-motion';

/**
 * Three-option selection for recognition phase
 */
export default function RecognitionOptions({ 
  cueImage, 
  options, // [{ id, image, isCorrect }, ...]
  onSelect,
  timeLimit = 4000,
  showTimeout,
  disabled
}) {
  const [timeRemaining, setTimeRemaining] = useState(timeLimit / 1000);
  const [selectedId, setSelectedId] = useState(null);
  
  useEffect(() => {
    const interval = setInterval(() => {
      setTimeRemaining(prev => Math.max(0, prev - 0.1));
    }, 100);
    
    return () => clearInterval(interval);
  }, []);
  
  const handleSelect = (option) => {
    if (disabled || selectedId) return;
    
    setSelectedId(option.id);
    setTimeout(() => {
      onSelect(option);
    }, 200); // Brief highlight before advancing
  };
  
  const progressPercent = (timeRemaining / (timeLimit / 1000)) * 100;
  const isUrgent = timeRemaining < 2;
  
  return (
    <div className="flex flex-col items-center gap-8">
      {/* Cue Image */}
      <div className="bg-white border-4 border-gray-300 rounded-2xl p-6 shadow-lg">
        <img 
          src={cueImage} 
          alt="Cue" 
          className="w-32 h-32 object-contain"
        />
      </div>
      
      {/* Timer Bar */}
      <div className="w-full max-w-2xl h-3 bg-gray-200 rounded-full overflow-hidden">
        <motion.div
          className={`h-full ${isUrgent ? 'bg-red-500' : 'bg-green-500'}`}
          initial={{ width: '100%' }}
          animate={{ width: `${progressPercent}%` }}
          transition={{ duration: 0.1 }}
        />
      </div>
      
      {/* Options */}
      <div className="flex flex-wrap justify-center gap-6">
        {options.map((option) => (
          <motion.button
            key={option.id}
            onClick={() => handleSelect(option)}
            disabled={disabled}
            whileHover={{ scale: disabled ? 1 : 1.05 }}
            whileTap={{ scale: disabled ? 1 : 0.95 }}
            className={`
              border-4 rounded-2xl p-4 transition-all
              ${selectedId === option.id 
                ? 'border-blue-500 bg-blue-50 ring-4 ring-blue-200' 
                : 'border-gray-300 bg-white hover:border-blue-400'
              }
              ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}
            `}
          >
            <img 
              src={option.image} 
              alt="Option"
              className="w-32 h-32 object-contain"
            />
          </motion.button>
        ))}
      </div>
      
      {/* Timeout Message */}
      {showTimeout && (
        <motion.div
          initial={{ scale: 0.8, opacity: 0 }}
          animate={{ scale: 1, opacity: 1 }}
          exit={{ scale: 0.8, opacity: 0 }}
          className="fixed inset-0 flex items-center justify-center 
                     bg-black bg-opacity-50 z-50"
        >
          <div className="bg-white px-12 py-8 rounded-2xl shadow-2xl">
            <p className="text-3xl font-bold text-orange-600">Too Late!</p>
          </div>
        </motion.div>
      )}
    </div>
  );
}

📊 Data Export Implementation

vpamDataExport.js

/**
 * Export VPAM test results in multiple formats
 */

/**
 * Export as JSON file
 */
export function exportJSON(data, ageGroup) {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const filename = `vpam_results_${ageGroup}_${timestamp}.json`;
  
  const jsonStr = JSON.stringify(data, null, 2);
  const blob = new Blob([jsonStr], { type: 'application/json' });
  downloadBlob(blob, filename);
}

/**
 * Export as CSV file
 */
export function exportCSV(data, ageGroup) {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const filename = `vpam_results_${ageGroup}_${timestamp}.csv`;
  
  // CSV Headers
  const headers = [
    'Trial_Number',
    'Phase',
    'Trial_In_Phase',
    'Cue_Item',
    'Target_Item',
    'Participant_Response',
    'Response_Time_ms',
    'Accuracy',
    'Response_Type',
    'Selected_Option',
    'Timestamp'
  ];
  
  // Convert trial data to CSV rows
  const rows = data.trialData.map(trial => [
    trial.trialNumber,
    trial.phase,
    trial.trialInPhase,
    trial.cueItem || 'N/A',
    trial.targetItem,
    trial.participantResponse || '',
    trial.responseTime || '',
    trial.responseAccuracy,
    trial.responseType,
    trial.selectedOption || 'N/A',
    trial.timestamp
  ]);
  
  // Combine headers and rows
  const csvContent = [
    headers.join(','),
    ...rows.map(row => row.map(escapeCSV).join(','))
  ].join('\n');
  
  const blob = new Blob([csvContent], { type: 'text/csv' });
  downloadBlob(blob, filename);
}

/**
 * Escape CSV values
 */
function escapeCSV(value) {
  if (value === null || value === undefined) return '';
  const str = String(value);
  if (str.includes(',') || str.includes('"') || str.includes('\n')) {
    return `"${str.replace(/"/g, '""')}"`;
  }
  return str;
}

/**
 * Trigger browser download
 */
function downloadBlob(blob, filename) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

/**
 * Export summary report (human-readable)
 */
export function exportSummaryReport(data, ageGroup) {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const filename = `vpam_summary_${ageGroup}_${timestamp}.txt`;
  
  const report = `
VISUAL PAIRED ASSOCIATES MEMORY TEST - SUMMARY REPORT
====================================================

Test Information:
-----------------
Age Group: ${ageGroup === 'adolescent' ? 'Adolescent (14-18)' : 'Adult (18-22)'}
Test Date: ${data.participantInfo.testDate}
Total Duration: ${data.participantInfo.totalDuration} minutes
Delay Duration: ${data.participantInfo.delayDurationActual} minutes

Overall Performance:
--------------------
Total Trials: ${data.responseCounts.totalTrials}
Total Responses: ${data.responseCounts.totalResponses}
Correct Responses: ${data.responseCounts.correctResponses}
Overall Accuracy: ${data.accuracy.overall.toFixed(2)}%

Phase-Specific Accuracy:
------------------------
Immediate Recall: ${data.accuracy.immediateRecall.toFixed(2)}%
Delayed Recall: ${data.accuracy.delayedRecall.toFixed(2)}%
Recognition: ${data.accuracy.recognition.toFixed(2)}%

Reaction Times (Mean):
----------------------
Immediate Recall: ${data.reactionTimes.immediateRecallMean}ms
Delayed Recall: ${data.reactionTimes.delayedRecallMean}ms
Recognition: ${data.reactionTimes.recognitionMean}ms

Memory Consolidation:
---------------------
Absolute Change: ${data.consolidation.absoluteChange.toFixed(2)}%
Percentage Change: ${data.consolidation.percentageChange.toFixed(2)}%
Items Retained: ${data.consolidation.itemsRetained} / 35

====================================================
  `.trim();
  
  const blob = new Blob([report], { type: 'text/plain' });
  downloadBlob(blob, filename);
}

🔍 Testing Checklist

Before Declaring Complete

  • All 11 screens functional and transition properly
  • Timing accuracy verified with stopwatch
    • Encoding: 3.5s (adolescent) / 2.5s (adult) per pair
    • Recall: 15s response window
    • Recognition: 4s response window
    • Delay: Exactly 15 minutes
  • Image loading works for all 210+ images
  • Response validation correctly identifies exact matches
  • Data recording captures all required fields
  • Timeout messages display correctly
  • Recognition randomization works (options in different positions)
  • Calculations correct for all metrics
  • Export functions produce valid JSON/CSV files
  • Age-specific content displays properly for both groups
  • Responsive design works on mobile (345px) to desktop
  • No console errors or warnings
  • Memory leaks prevented (proper cleanup in useEffect)
  • Keyboard navigation works (Enter to submit, Tab through options)
  • Accessibility basics (alt text, ARIA labels, focus indicators)

🎓 Integration with Existing Project

Update TestSelectionScreen

// components/screens/TestSelectionScreen.jsx

const tests = [
  {
    id: 'rit',
    name: 'Response Inhibition Task',
    description: 'Go/No-Go test measuring impulse control',
    component: ResponseInhibitionTest,
    estimatedTime: '8-10 minutes'
  },
  {
    id: 'cft',
    name: 'Cognitive Flexibility Test',
    description: 'Tests ability to switch between rules',
    component: CognitiveFlexibilityTest,
    estimatedTime: '10-12 minutes'
  },
  {
    id: 'vpam',  // NEW
    name: 'Visual Paired Associates Memory',
    description: 'Tests associative memory and retention',
    component: VisualPairedAssociatesTest,
    estimatedTime: '30-35 minutes'
  }
];

Update testsConfig.js

// constants/testsConfig.js

export const TESTS_CONFIG = {
  rit: {
    name: 'Response Inhibition Task',
    shortName: 'RIT',
    route: '/rit',
    color: 'blue'
  },
  cft: {
    name: 'Cognitive Flexibility Test',
    shortName: 'CFT',
    route: '/cft',
    color: 'purple'
  },
  vpam: {  // NEW
    name: 'Visual Paired Associates Memory',
    shortName: 'VPAM',
    route: '/vpam',
    color: 'green'
  }
};

📝 Final Notes

Research Validity Reminders

  • This is a standardized memory test - ANY deviation from specifications invalidates research data
  • Timing must be precise to the millisecond where possible
  • Trial order is predetermined, not randomized - follow PDF exactly
  • Response validation must be strict (exact item names only)
  • Data export must include ALL metrics for statistical analysis

Performance Optimization

  • Preload ALL images before starting encoding phase
  • Use React.memo() for heavy components
  • Debounce text input validation (but record raw input)
  • Clear timers and intervals properly to prevent memory leaks
  • Consider using Web Workers for data calculations if needed

Accessibility Considerations

  • Keyboard navigation for all inputs
  • Screen reader compatibility (ARIA labels)
  • High contrast mode support
  • Focus indicators clearly visible
  • Text size adjustable (relative units)

Future Enhancements (Optional)

  • Practice trials for recall/recognition phases
  • Audio cues (optional mode)
  • Multi-language support
  • Adaptive difficulty (not standard, research variant)
  • Real-time data sync to server
  • Progress save/resume functionality

Development Completion Criteria

The VPAM test is considered complete when:

  1. All 11 screens implemented and functional
  2. All timing specifications met precisely
  3. All 210+ images loaded and displayed correctly
  4. Data recording captures all required metrics
  5. Export functions produce valid, complete data files
  6. Age-specific content displays correctly
  7. Responsive design works across all screen sizes
  8. No bugs, errors, or console warnings
  9. Code is clean, commented, and maintainable
  10. Integration with existing project seamless
  11. Testing checklist 100% verified
  12. Documentation updated

IMPORTANT REMINDERS:

  • ONE SCREEN AT A TIME - wait for:**
  • components/screens/VPAM/IntroScreen.jsx
  • constants/vpamInstructions.js

Content:

// Generic welcome message (shown to all)
"In this game, you will see pictures of everyday things. Some of these 
pictures will be shown together as pairs. Your task is to pay close 
attention and try to remember which ones are being presented as pairs, 
because later we will test your memory in different ways. Sometimes you 
will have to write the missing item, and sometimes you will choose it 
from a few options. Let's get started!"

State to initialize:

  • ageGroup (null → 'adolescent' or 'adult')
  • currentScreen ('intro')

Screen 2: EncodingInstructionScreen

Purpose: Explain the learning phase before showing pairs

Components needed:

  • Instruction text display
  • Example pair visualization
  • Duration explanation
  • "Let's Go!" button

Required files:

  • components/screens/VPAM/EncodingInstructionScreen.jsx

Instructions:

Adolescents:

"You will see two pictures appear inside a box on the screen. Just look 
carefully and try to remember which pictures are being presented as pairs. 
You don't need to press anything. Each pair will disappear after a few 
seconds, so focus while it is on the screen."

Adults:

"You will see two items presented together inside a box. Your task is to 
observe carefully and remember which items are being presented as pairs. 
You don't need to press anything. Each pair will only remain on screen 
briefly, so focus while it is shown."

Screen 3: EncodingScreen

Purpose: Display all 35 pairs for learning

Components needed:

  • White box container (centered)
  • Two image displays (side by side)
  • Automatic timer
  • Progress indicator (optional: "Pair 12 of 35")
  • Auto-advance logic

Required files:

  • components/screens/VPAM/EncodingScreen.jsx
  • components/shared/VPAM/PairDisplay.jsx
  • hooks/vpam/useEncodingScreen.js
  • utils/vpamTimer.js

Critical logic:

// For each pair in sequence:
1. Display both images together
2. Start timer (3.5s adolescent / 2.5s adult)
3. Wait for timer completion (NO user input)
4. Clear screen briefly
5. Load next pair
6. After pair 35  transition to Immediate Recall

Timing precision:

  • Use setTimeout or setInterval
  • Track exposure time per pair
  • Record timestamp for each pair shown

Screen 4: ImmediateRecallInstructionScreen

Purpose: Explain the typing recall task

Components needed:

  • Instruction text display
  • Example with visual
  • Response format explanation
  • "Let's Go!" button

Required files:

  • components/screens/VPAM/ImmediateRecallInstructionScreen.jsx

Instructions:

Adolescents:

"Great job learning the pairs! Now, one item from each pair will appear 
by itself. Your task is to write the item that goes with it. Try to answer 
quickly — you'll have a short time for each one.

Remember: write the exact item that was paired. Don't just write a general 
category, color, or size — focus on the specific item.

Example: If you see 🍎 Apple and it was paired with 🪑 Chair, write 'Chair', 
not 'furniture' or 'brown chair' or 'big chair'."

Adults:

"You've just seen all the pairs. Now, a single item from each pair will 
appear alone. Your task is to type or write the item that was originally 
paired with it. Respond quickly — each trial has a time limit.

Type the exact item that was paired. Do not write a general category, color, 
or size — focus on the specific item.

Example: If the cue is 🖊️ Pen and it was paired with 🐘 Elephant, type 
'Elephant', not 'animal', 'creature', or 'grey elephant'."

Screen 5: ImmediateRecallScreen

Purpose: Test immediate memory with typed responses

Components needed:

  • Cue image display (white box, centered)
  • Text input field (below cue)
  • Submit button
  • Timer countdown (optional visual)
  • "Time is up!" message overlay
  • Trial counter

Required files:

  • components/screens/VPAM/ImmediateRecallScreen.jsx
  • components/shared/VPAM/RecallInput.jsx
  • hooks/vpam/useRecallScreen.js

Critical logic:

// For each trial:
1. Display cue image
2. Start 15-second timer
3. Show text input field
4. Listen for:
   - Submit button click
   - Enter key press
   - Timer expiration
5. Validate response against correct answer
6. Record:
   - Response text
   - Reaction time (ms from cue to submit)
   - Accuracy (1/0)
7. If timeout  show "Time is up!" for 1s
8. Clear input and load next trial
9. After trial 35  transition to Delay Interval

Response validation:

function validateResponse(userInput, correctAnswer) {
  // Normalize both strings
  const normalized = userInput.toLowerCase().trim();
  const correct = correctAnswer.toLowerCase().trim();
  
  // Exact match required
  return normalized === correct;
  
  // Optional: Allow minor typos (Levenshtein distance ≤ 1)
  // But be careful - too lenient affects data validity
}

Screen 6: DelayIntervalScreen

Purpose: Transition message and launch filler tasks

Components needed:

  • Transition message display
  • Automatic task chaining
  • Timer tracking (15 minutes total)
  • Progress indicator (optional)

Required files:

  • components/screens/VPAM/DelayIntervalScreen.jsx
  • hooks/vpam/useDelayInterval.js

Transition messages:

Adolescents:

"Great job looking at all the pairs! Before we test your memory, you'll 
play a few short brain-teaser games. These games are like a quick workout 
for your mind. Once you're done, we'll bring you back to check how many 
pairs you can remember!"

Adults:

"Well done—you've completed the first part. Next, you'll do a few short 
thinking activities designed to keep your mind active while your memory 
continues to work in the background. After that, we'll return to test how 
well you remember the pairs."

Filler task sequence:

// Import and chain existing components:
1. <ResponseInhibitionTest /> (Go/No-Go)
2. <CognitiveFlexibilityTest /> (CFT)
3. <StroopTest /> (Attention - if available)

// Track elapsed time
// At 15:00 → auto-launch Delayed Recall

Screen 7: DelayedRecallInstructionScreen

Purpose: Re-explain recall task after delay

Components needed:

  • "Welcome back" message
  • Reminder of task rules
  • "Let's Go!" button

**Required files