diff --git a/src/components/gameMaster/mapEditor/partials/TileList.vue b/src/components/gameMaster/mapEditor/partials/TileList.vue
index bd7afa1..4a21a32 100644
--- a/src/components/gameMaster/mapEditor/partials/TileList.vue
+++ b/src/components/gameMaster/mapEditor/partials/TileList.vue
@@ -29,7 +29,7 @@
                       :src="`${config.server_endpoint}/textures/tiles/${group.parent.id}.png`"
                       :alt="group.parent.name"
                       @click="openGroup(group)"
-                      @load="() => processTile(group.parent)"
+                      @load="() => tileProcessor.processTile(group.parent)"
                       :class="{
                         'border-cyan shadow-lg': isActiveTile(group.parent),
                         'border-transparent hover:border-gray-300': !isActiveTile(group.parent)
@@ -88,13 +88,14 @@
 import config from '@/application/config'
 import type { Tile } from '@/application/types'
 import { useMapEditorComposable } from '@/composables/useMapEditorComposable'
+import { useTileProcessingComposable } from '@/composables/useTileProcessingComposable'
 import { TileStorage } from '@/storage/storages'
-import { liveQuery } from 'dexie'
-import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
+import { computed, onMounted, onUnmounted, ref } from 'vue'
 
 const isOpen = ref(false)
 const tileStorage = new TileStorage()
 const mapEditor = useMapEditorComposable()
+const tileProcessor = useTileProcessingComposable()
 const searchQuery = ref('')
 const selectedTags = ref<string[]>([])
 const tileCategories = ref<Map<string, string>>(new Map())
@@ -121,7 +122,7 @@ const groupedTiles = computed(() => {
   })
 
   filteredTiles.forEach((tile) => {
-    const parentGroup = groups.find((group) => areTilesRelated(group.parent, tile))
+    const parentGroup = groups.find((group) => tileProcessor.areTilesRelated(group.parent, tile))
     if (parentGroup && parentGroup.parent.id !== tile.id) {
       parentGroup.children.push(tile)
     } else {
@@ -132,32 +133,6 @@ const groupedTiles = computed(() => {
   return groups
 })
 
-const tileColorData = ref<Map<string, { r: number; g: number; b: number }>>(new Map())
-const tileEdgeData = ref<Map<string, number>>(new Map())
-
-function areTilesRelated(tile1: Tile, tile2: Tile): boolean {
-  const colorSimilarityThreshold = 30 // Adjust this value as needed
-  const edgeComplexitySimilarityThreshold = 20 // Adjust this value as needed
-
-  const color1 = tileColorData.value.get(tile1.id)
-  const color2 = tileColorData.value.get(tile2.id)
-  const edge1 = tileEdgeData.value.get(tile1.id)
-  const edge2 = tileEdgeData.value.get(tile2.id)
-
-  if (!color1 || !color2 || edge1 === undefined || edge2 === undefined) {
-    return false
-  }
-
-  const colorDifference = Math.sqrt(Math.pow(color1.r - color2.r, 2) + Math.pow(color1.g - color2.g, 2) + Math.pow(color1.b - color2.b, 2))
-
-  const edgeComplexityDifference = Math.abs(edge1 - edge2)
-
-  const namePrefix1 = tile1.name.split('_')[0]
-  const namePrefix2 = tile2.name.split('_')[0]
-
-  return colorDifference <= colorSimilarityThreshold && edgeComplexityDifference <= edgeComplexitySimilarityThreshold && namePrefix1 === namePrefix2
-}
-
 const toggleTag = (tag: string) => {
   if (selectedTags.value.includes(tag)) {
     selectedTags.value = selectedTags.value.filter((t) => t !== tag)
@@ -166,59 +141,6 @@ const toggleTag = (tag: string) => {
   }
 }
 
-function processTile(tile: Tile) {
-  const img = new Image()
-  img.crossOrigin = 'Anonymous'
-  img.onload = () => {
-    const canvas = document.createElement('canvas')
-    const ctx = canvas.getContext('2d')
-    canvas.width = img.width
-    canvas.height = img.height
-    ctx!.drawImage(img, 0, 0, img.width, img.height)
-
-    const imageData = ctx!.getImageData(0, 0, canvas.width, canvas.height)
-    tileColorData.value.set(tile.id, getDominantColor(imageData))
-    tileEdgeData.value.set(tile.id, getEdgeComplexity(imageData))
-  }
-  img.src = `${config.server_endpoint}/textures/tiles/${tile.id}.png`
-}
-
-function getDominantColor(imageData: ImageData) {
-  let r = 0,
-    g = 0,
-    b = 0,
-    total = 0
-
-  for (let i = 0; i < imageData.data.length; i += 4) {
-    if (imageData.data[i + 3] > 0) {
-      // Only consider non-transparent pixels
-      r += imageData.data[i]
-      g += imageData.data[i + 1]
-      b += imageData.data[i + 2]
-      total++
-    }
-  }
-
-  return {
-    r: Math.round(r / total),
-    g: Math.round(g / total),
-    b: Math.round(b / total)
-  }
-}
-
-function getEdgeComplexity(imageData: ImageData) {
-  let edgePixels = 0
-  for (let y = 0; y < imageData.height; y++) {
-    for (let x = 0; x < imageData.width; x++) {
-      const i = (y * imageData.width + x) * 4
-      if (imageData.data[i + 3] > 0 && (x === 0 || y === 0 || x === imageData.width - 1 || y === imageData.height - 1 || imageData.data[i - 1] === 0 || imageData.data[i + 7] === 0)) {
-        edgePixels++
-      }
-    }
-  }
-  return edgePixels
-}
-
 function getTileCategory(tile: Tile): string {
   return tileCategories.value.get(tile.id) || ''
 }
@@ -239,22 +161,19 @@ function isActiveTile(tile: Tile): boolean {
   return mapEditor.selectedTile.value === tile.id
 }
 
-let subscription: any = null
+onMounted(async () => {
+  tiles.value = await tileStorage.getAll()
+  const initialBatchSize = 20
+  const initialTiles = tiles.value.slice(0, initialBatchSize)
+  initialTiles.forEach((tile) => tileProcessor.processTile(tile))
 
-onMounted(() => {
-  subscription = liveQuery(() => tileStorage.liveQuery()).subscribe({
-    next: (result) => {
-      tiles.value = result
-    },
-    error: (error) => {
-      console.error('Failed to fetch tiles:', error)
-    }
-  })
+  // Process remaining tiles in background
+  setTimeout(() => {
+    tiles.value.slice(initialBatchSize).forEach((tile) => tileProcessor.processTile(tile))
+  }, 1000)
 })
 
 onUnmounted(() => {
-  if (subscription) {
-    subscription.unsubscribe()
-  }
+  tileProcessor.cleanup()
 })
 </script>