diff --git a/Cargo.lock b/Cargo.lock
index c711a27b35307563d7aec2a890cc1206c2d529e3..bee549ef84e25a6488d1be3859d7af598b8f06e0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1052,6 +1052,7 @@ dependencies = [
  "imageproc",
  "log",
  "musicbrainz_rs",
+ "rayon",
  "read_color",
  "reqwest 0.10.4",
  "serde",
diff --git a/Cargo.toml b/Cargo.toml
index 69e0f47050de9c760b0e6c8f9c00b4273b9c7a8a..3a667d6155a4c182fbac0b0d56c8d44525a19b6c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ image = "0.23.2"            # Creating parsing and manipulating images.
 imageproc = "0.20.0"        # Bunch of of filters and algos to process images.
 read_color = "1.0.0"        # What's that? Idunno.
 musicbrainz_rs = "0.2.0"
-
+rayon = "1.1"               # Parallelizing processing.
 
 [dependencies.fern]             # Simple, efficient logging.
 version = "0.5"
diff --git a/lib/img_processors/mod.rs b/lib/img_processors/mod.rs
index adf94250722c93c2b0b0d829a265a9e11ccef942..0cedda44fd780b8dd8b114649cb0cffc27db86b5 100644
--- a/lib/img_processors/mod.rs
+++ b/lib/img_processors/mod.rs
@@ -1,10 +1,13 @@
 use image::imageops::overlay;
-use image::{DynamicImage, GenericImageView};
+use image::{DynamicImage, GenericImageView, RgbaImage};
 
 use crate::config::image_processors::ProcessorParams;
 use crate::config::Config;
 use crate::display::get_max_resolution;
-use crate::result::{MBGError, MBGResult};
+use crate::result::MBGResult;
+use rayon::prelude::*;
+use std::collections::HashMap;
+use std::collections::hash_map::RandomState;
 
 pub mod blur;
 pub mod border;
@@ -63,40 +66,74 @@ impl Default for Processors {
     }
 }
 
+pub fn process_layer(
+    screen_height: i32,
+    screen_width: i32,
+    image: DynamicImage,
+    processors: Vec<ProcessorParams>,
+) -> MBGResult<DynamicImage> {
+    let mut image = image;
+    for processor_params in processors {
+        let processor = mapper(processor_params, screen_height, screen_width);
+        image = processor.process(&image)?;
+        debug!("Processor applied")
+    }
+    Ok(image)
+}
+
 pub fn process_image(img_bytes: Vec<u8>) -> MBGResult<DynamicImage> {
     let conf = Config::get_props();
     let (screen_height, screen_width) = get_max_resolution()?;
     debug!("Maximum resolution: {}x{}", screen_width, screen_height);
     let image = image::load_from_memory(img_bytes.as_slice())?;
-    let mut layers = vec![image.clone()];
-    for params in conf.processors.into_iter() {
-        let processor = mapper(params, screen_height, screen_width);
-        if let Some(layer_img) = layers.get(processor.get_layer()) {
-            layers[processor.get_layer()] = processor.process(layer_img)?;
-        } else {
-            layers.push(processor.process(&image.clone())?);
-        }
-        debug!("Filter applied");
-    }
-    if layers.is_empty() {
-        return Err(MBGError::ImageError(String::from(
-            "No processors was found.",
-        )));
-    }
-    if conf.blender.is_empty() {
-        Ok(layers[0].clone())
-    } else {
-        blend_layers(layers, conf.blender)
-    }
+    let layers_conf =
+        conf.processors
+            .iter()
+            .fold(HashMap::<usize, Vec<ProcessorParams>>::new(), |mut acc, x| {
+                let new_processors = if let Some(current) = acc.get(&x.layer) {
+                    let mut mutable = current.clone();
+                    mutable.push(x.clone());
+                    mutable
+                } else {
+                    vec![x.clone()]
+                };
+                acc.insert(x.layer, new_processors);
+                acc
+            });
+    let processed_layers = layers_conf
+        .par_iter()
+        .map(|(layer, processors)| {
+            let img = process_layer(
+                screen_height,
+                screen_width,
+                image.clone(),
+                processors.to_vec(),
+            );
+            (layer, img)
+        })
+        .collect::<HashMap<_, _, RandomState>>();
+    blend_layers(screen_height, screen_width, processed_layers, conf.blender)
 }
 
-pub fn blend_layers(layers: Vec<DynamicImage>, blender: Vec<u8>) -> MBGResult<DynamicImage> {
-    let mut res = layers[0].clone();
+pub fn blend_layers<S: std::hash::BuildHasher>(
+    height: i32,
+    width: i32,
+    layers: HashMap<&usize, MBGResult<DynamicImage>, S>,
+    blender: Vec<u8>,
+) -> MBGResult<DynamicImage> {
+    let mut res = DynamicImage::ImageRgba8(RgbaImage::new(width as u32, height as u32));
     for index in blender.into_iter() {
-        if let Some(layer) = layers.get(usize::from(index)) {
-            let pos_x = (res.width() / 2) - (layer.width() / 2);
-            let pos_y = (res.height() / 2) - (layer.height() / 2);
-            overlay(&mut res, layer, pos_x, pos_y);
+        if let Some(layer) = layers.get(&usize::from(index)) {
+            match layer {
+                Ok(image) => {
+                    let pos_x = (res.width() / 2) - (image.width() / 2);
+                    let pos_y = (res.height() / 2) - (image.height() / 2);
+                    overlay(&mut res, image, pos_x, pos_y);
+                }
+                Err(error) => {
+                    error!("{}", error);
+                }
+            }
         }
     }
     Ok(res)