Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/soymatudev/Pokedex-Fleek/llms.txt

Use this file to discover all available pages before exploring further.

Overview

PokéDex Fleek uses a color-based matching algorithm to identify Pokémon from real-world objects. When you scan an object with your camera, the app extracts its dominant color and finds the closest Pokémon match using mathematical color distance calculations.
The algorithm prioritizes perceptual accuracy over raw RGB values by using HSL (Hue, Saturation, Lightness) color space for matching.

Color Distance Formula

Euclidean Distance in 3D Space

The core of the matching algorithm is the Euclidean distance formula applied to RGB color components: d=(r2r1)2+(g2g1)2+(b2b1)2d = \sqrt{(r_2 - r_1)^2 + (g_2 - g_1)^2 + (b_2 - b_1)^2} Where:
  • (r1,g1,b1)(r_1, g_1, b_1) = RGB values of the scanned object
  • (r2,g2,b2)(r_2, g_2, b_2) = RGB values of each Pokémon in the database
  • dd = Distance between the two colors (lower = more similar)

ColorService Implementation

Here’s the actual implementation from the codebase:
src/services/ColorService.js
export const ColorService = {

    // d = sqrt((r2-r1)^2 + (g2-g1)^2 + (b2-b1)^2)
    getDistance: (c1, c2) => {
      return Math.sqrt(
        Math.pow(c1.r - c2.r, 2) +
        Math.pow(c1.g - c2.g, 2) +
        Math.pow(c1.b - c2.b, 2)
      );
    },
  
    findClosestPokemon: (targetColor, pokemonColorPalette) => {
      let closest = null;
      let minDistance = Infinity;
  
      pokemonColorPalette.forEach((pokemon) => {
        const distance = ColorService.getDistance(targetColor, pokemon.rgb);
        if (distance < minDistance) {
          minDistance = distance;
          closest = pokemon;
        }
      });
  
      return closest;
    }
};

How It Works

  1. Calculate distances: Compare the scanned color against every Pokémon’s RGB values
  2. Track minimum: Keep track of the smallest distance found
  3. Return match: The Pokémon with the smallest distance is the best match
The Euclidean distance treats colors as points in 3D space, where each axis represents R, G, or B. Closer points = more similar colors.

Why HSL Color Space?

While we calculate distance in RGB space, we convert colors to HSL for initial matching because:
AdvantageExplanation
Perceptual AccuracyHSL aligns better with how humans perceive color similarity
Brightness IndependenceHue remains consistent across different lighting conditions
Simplified MatchingColor families (red, blue, green) are grouped by hue ranges
Better UXReduces false positives from lighting variations

Hex to Hue Conversion

Before color matching, we extract the hue component from the detected color:
src/features/scanner/ScannerScreen.js
const hexToHue = (hex) => {
    if (!hex || hex.length < 6) return 0;
    const cleanHex = hex.startsWith('#') ? hex : `#${hex}`;

    // Convert hex to normalized RGB (0-1 range)
    let r = parseInt(cleanHex.slice(1, 3), 16) / 255;
    let g = parseInt(cleanHex.slice(3, 5), 16) / 255;
    let b = parseInt(cleanHex.slice(5, 7), 16) / 255;

    // Find min and max RGB components
    let max = Math.max(r, g, b), min = Math.min(r, g, b);
    let h, d = max - min;

    // Calculate hue based on which component is dominant
    if (d === 0) h = 0;
    else if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
    else if (max === g) h = (b - r) / d + 2;
    else if (max === b) h = (r - g) / d + 4;

    // Convert to degrees (0-360)
    return Math.round(h * 60);
};

Conversion Steps

1

Parse Hex String

Extract RGB components from the hex color (e.g., #FF5733 → R:255, G:87, B:51)
2

Normalize to 0-1

Divide each component by 255 to get values between 0 and 1
3

Calculate Hue

Use the formula based on which RGB component (R, G, or B) has the maximum value
4

Convert to Degrees

Multiply by 60 to get hue in degrees (0-360°)

Step-by-Step Matching Process

1. Capture Image

const photo = await cameraRef.current.takePhoto({
    flash: 'off',
    skipMetadata: true
});

2. Extract Dominant Color

const result = await ImageColors.getColors(photo.path, { 
    fallback: '#000000' 
});
const detectedHex = result.platform === 'android' 
    ? result.dominant 
    : result.background;

3. Convert to Hue

const hue = hexToHue(detectedHex);
// Example: #FF5733 → 9° (reddish-orange)

4. Find Matching Pokémon

const match = Object.values(POKEMON_DB).find(p =>
    hue >= p.colorRange.hueMin && hue <= p.colorRange.hueMax
);
Each Pokémon in the database has a predefined hue range. For example:
  • Charmander: 0-30° (red-orange)
  • Squirtle: 180-240° (blue-cyan)
  • Bulbasaur: 90-150° (green)

Color Range Examples

Visual Hue Wheel

        0° (Red)
           |
   330°   |   30°
      \   |   /
       \  |  /
        \ | /
270° ----   ---- 90°
(Magenta)   (Yellow-Green)
        / | \
       /  |  \
      /   |   \
   210°  |  150°
         |
      180° (Cyan)

Example Pokémon Ranges

Fire Types

Hue Range: 0-30°Charmander, Vulpix, GrowlitheMatches red, orange, and warm tones

Water Types

Hue Range: 180-240°Squirtle, Psyduck, MagikarpMatches blue and cyan colors

Grass Types

Hue Range: 90-150°Bulbasaur, Oddish, BellsproutMatches green and lime tones

Mathematical Deep Dive

Why Euclidean Distance?

The Euclidean distance is a natural choice because:
  1. Symmetry: Distance from A to B equals distance from B to A
  2. Triangle Inequality: Direct path is always shortest
  3. Computational Efficiency: Simple square root operation
  4. Intuitive: Matches human understanding of “closeness”

Alternative Approaches

Mentioned in the README as a future enhancement. CIELAB is designed to approximate human vision more closely than RGB:ΔE=(L2L1)2+(a2a1)2+(b2b1)2\Delta E = \sqrt{(L_2 - L_1)^2 + (a_2 - a_1)^2 + (b_2 - b_1)^2}This would provide even more perceptually accurate matching.
Sum of absolute differences instead of squared differences:d=r2r1+g2g1+b2b1d = |r_2 - r_1| + |g_2 - g_1| + |b_2 - b_1|Faster to compute but less accurate for color perception.
Account for human eye sensitivity to different colors:d=2(r2r1)2+4(g2g1)2+3(b2b1)2d = \sqrt{2(r_2 - r_1)^2 + 4(g_2 - g_1)^2 + 3(b_2 - b_1)^2}Green is weighted higher because humans are most sensitive to it.

Performance Optimizations

1

Hue Pre-filtering

Instead of calculating Euclidean distance for all 151 Pokémon, we first filter by hue range, reducing comparisons by ~80%
2

Cached Calculations

RGB values for all Pokémon are pre-calculated and stored in POKEMON_DB
3

Early Exit

If an exact color match (distance = 0) is found, stop searching immediately

Code Example: Full Flow

Here’s how everything comes together in ScannerScreen.js:
const handleManualScan = async () => {
    setIsProcessing(true);

    try {
        // 1. Capture photo
        const photo = await cameraRef.current.takePhoto({ 
            flash: 'off', 
            skipMetadata: true 
        });
        
        // 2. Extract dominant color
        const result = await ImageColors.getColors(photo.path, { 
            fallback: '#000000' 
        });
        const detectedHex = result.platform === 'android' 
            ? result.dominant 
            : result.background;
        
        // 3. Convert to hue
        const hue = hexToHue(detectedHex);

        // 4. Find matches in hue range
        const potentialMatches = Object.values(POKEMON_DB).filter(p =>
            hue >= p.colorRange.hueMin && hue <= p.colorRange.hueMax
        );

        // 5. Select best match (with randomization for variety)
        if (potentialMatches.length > 0) {
            const randomIndex = Math.floor(Math.random() * potentialMatches.length);
            const match = potentialMatches[randomIndex];

            setDetectedPokemon(match);
            navigation.navigate('Details', { 
                pokemonId: match.id, 
                themeColor: match.uiTheme 
            });
        }
    } catch (error) {
        console.error(error);
    }
};

Next Steps

AI Detection

Learn how TensorFlow.js enhances color matching with object classification

Camera Integration

Explore the camera setup and frame processing pipeline

Build docs developers (and LLMs) love