diff --git a/.gitignore b/.gitignore
index 9b06c6b1d2000ef1f32d00778b7082175bf8b419..e0b05f96af902e3cdfb027cfa928654340ad4942 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@ Cargo.lock
 /target
 /res/roms.prop
 /frontends/*/target
+
+/src/gen.rs
diff --git a/Cargo.toml b/Cargo.toml
index d40bb1e7ed3fd02f69e2abb06d39d2c08c0ac0bc..45a76b8cf6e57af8ec8240c8b7d4497f3a75ebcb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,8 @@ repository = "https://github.com/joamag/boytacean"
 keywords = ["gameboy", "emulator", "rust"]
 edition = "2018"
 exclude = ["/frontends", "/res/roms", "/res/screens", "/res/videos"]
+build = "build.rs"
+readme = "README.md"
 
 [lib]
 crate-type = ["cdylib", "rlib"]
@@ -19,6 +21,11 @@ debug = []
 [dependencies]
 wasm-bindgen = { version = "0.2", optional = true }
 
+[build-dependencies]
+chrono = "0.4"
+num_cpus = "1"
+regex = "1"
+
 [profile.release]
 debug = false
 lto = true
diff --git a/README.md b/README.md
index 679c4939aafd6871677e0d2fad1f4bf0899fea38..dc449caa07a4a76afd5b754904794ae391c47c38 100644
--- a/README.md
+++ b/README.md
@@ -94,7 +94,7 @@ To get some information about the resources that inspired me through the emulati
 
 ## License
 
-Boytacian is currently licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/).
+Boytacean is currently licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/).
 
 ## Build Automation
 
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..67bcc46753a20751db2886bacf7c80d585e241ca
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,153 @@
+/// Build script (https://doc.rust-lang.org/cargo/reference/build-scripts.html)
+/// This script is executed as the first step in the compilation process.
+/// Here we export metadata constants to a `constants/generated.rs` file which is then
+/// imported and used by the remaining crate.
+///
+/// # Examples
+///
+/// In C you can use the preprocessor macro `__DATE__` to save the compilation date like:
+///
+/// ```c
+/// #define COMPILATION_DATE __DATE__
+/// ```
+///
+/// Rust does not have such preprocessor macros, so we use this script and do:
+///
+/// ```rust
+/// let now_utc = chrono::Utc::now();
+/// write_str_constant(
+///     &mut file,
+///     "COMPILATION_DATE",
+///     &format!("{}", now_utc.format("%b %d %Y")),
+/// );
+/// ```
+use chrono::Utc;
+use regex::Regex;
+use std::fs::{File, OpenOptions};
+use std::io::Write;
+use std::path::Path;
+use std::process::Command;
+use std::str;
+
+const BUILD_OUT_FILE: &str = "gen.rs";
+const SOURCE_DIR: &str = "./src";
+
+fn main() {
+    // in case we're running under docs.rs then we must return the control
+    // flow immediately as it's not possible to generate files under the
+    // expected read only file system present in `docs.rs` build environment
+    if std::env::var("DOCS_RS").is_ok() {
+        return;
+    }
+
+    // opens the target destination file panicking with a proper message in
+    // case it was not possible to open it (eg: directory inexistent)
+    let dest_path = Path::new(SOURCE_DIR).join(Path::new(BUILD_OUT_FILE));
+    let mut file = OpenOptions::new()
+        .truncate(true)
+        .write(true)
+        .create(true)
+        .open(dest_path)
+        .unwrap_or_else(|_| panic!("Can't open '{}'", BUILD_OUT_FILE));
+
+    let module_doc_string = "//! Global constants, such as compiler version used, algorithms, compression and filters supported and others\n";
+    writeln!(file, "{}", module_doc_string).unwrap();
+
+    let generated_annotation = "// @generated\n";
+    writeln!(file, "{}", generated_annotation).unwrap();
+
+    let now_utc = Utc::now();
+    write_str_constant(
+        &mut file,
+        "COMPILATION_DATE",
+        &format!("{}", now_utc.format("%b %d %Y")),
+    );
+    write_str_constant(
+        &mut file,
+        "COMPILATION_TIME",
+        &format!("{}", now_utc.format("%H:%M:%S")),
+    );
+
+    write_str_constant(
+        &mut file,
+        "VERSION",
+        option_env!("CARGO_PKG_VERSION").unwrap_or("UNKNOWN"),
+    );
+
+    write_str_constant(&mut file, "COMPILER", "rustc");
+
+    let compiler_version = Command::new("rustc")
+        .arg("--version")
+        .output()
+        .ok()
+        .and_then(|output| String::from_utf8(output.stdout).ok())
+        .unwrap_or_else(|| "UNKNOWN".to_string());
+    let re = Regex::new("rustc ([\\d.\\d.\\d]*)").unwrap();
+    let compiler_version = re
+        .captures(&compiler_version)
+        .unwrap()
+        .get(1)
+        .unwrap()
+        .as_str();
+    write_str_constant(&mut file, "COMPILER_VERSION", compiler_version);
+
+    let mut features = vec!["cpu"];
+
+    if cfg!(feature = "wasm-extension") {
+        features.push("wasm")
+    }
+
+    if cfg!(feature = "python-extension") {
+        features.push("python")
+    }
+
+    write_vec_constant(&mut file, "FEATURES", features);
+
+    write_str_constant(
+        &mut file,
+        "PLATFORM_CPU_BITS",
+        &(std::mem::size_of::<usize>() * 8).to_string(),
+    );
+
+    write_constant(&mut file, "DEFAULT_THREAD_POOL_SIZE", num_cpus::get());
+    write_constant(&mut file, "MAX_THREAD_POOL_SIZE", num_cpus::get() * 10);
+}
+
+fn write_constant<T>(file: &mut File, key: &str, val: T)
+where
+    T: std::fmt::Display,
+{
+    writeln!(
+        file,
+        "pub const {}: {} = {};",
+        key,
+        std::any::type_name::<T>(),
+        val
+    )
+    .unwrap_or_else(|_| panic!("Failed to write '{}' to 'build_constants.rs'", key));
+}
+
+fn write_str_constant(file: &mut File, key: &str, val: &str) {
+    writeln!(file, "pub const {}: &str = \"{}\";", key, val)
+        .unwrap_or_else(|_| panic!("Failed to write '{}' to 'build_constants.rs'", key));
+}
+
+fn write_vec_constant<T>(file: &mut File, key: &str, vec: Vec<T>)
+where
+    T: std::fmt::Display,
+{
+    let mut list_str = String::new();
+    for value in &vec {
+        list_str.push_str(&format!("\"{}\", ", value))
+    }
+    list_str.pop();
+    writeln!(
+        file,
+        "pub const {}: [{}; {}] = [{}];",
+        key,
+        std::any::type_name::<T>(),
+        vec.len(),
+        list_str
+    )
+    .unwrap_or_else(|_| panic!("Failed to write '{}' to 'build_constants.rs'", key));
+}
diff --git a/frontends/web/react/components/help/help.tsx b/frontends/web/react/components/help/help.tsx
index d9003320f924889b6ecee3f503706381950a40e5..42ea60c648b1ba260e1f9f6047485e595cbaae47 100644
--- a/frontends/web/react/components/help/help.tsx
+++ b/frontends/web/react/components/help/help.tsx
@@ -78,7 +78,9 @@ export const HelpFaqs: FC = () => (
             play around 90% of the Game Boy games.
         </p>
         <h3>Why there's no sound?</h3>
-        <p>It's under development, I'm hopping to have it before end of 2023.</p>
+        <p>
+            It's under development, I'm hopping to have it before end of 2023.
+        </p>
         <h3>Can I use my Xbox One/PS4/PS5 Gamepad?</h3>
         <p>
             Yes, just plug it in, press a button, and you're ready to go.
diff --git a/frontends/web/ts/gb.ts b/frontends/web/ts/gb.ts
index 5b6b6f6cf6b67e75a17d365273dd37f5fb7d5a60..b954d9472504e12338fccb0dc8b0d44af658bc20 100644
--- a/frontends/web/ts/gb.ts
+++ b/frontends/web/ts/gb.ts
@@ -1,5 +1,7 @@
 import {
     BenchmarkResult,
+    Compilation,
+    Compiler,
     Emulator,
     EmulatorBase,
     Entry,
@@ -511,6 +513,22 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
         };
     }
 
+    get compiler(): Compiler | null {
+        if (!this.gameBoy) return null;
+        return {
+            name: this.gameBoy.get_compiler(),
+            version: this.gameBoy.get_compiler_version()
+        };
+    }
+
+    get compilation(): Compilation | null {
+        if (!this.gameBoy) return null;
+        return {
+            date: this.gameBoy.get_compilation_date(),
+            time: this.gameBoy.get_compilation_time()
+        };
+    }
+
     get framerate(): number {
         return this.fps;
     }
diff --git a/node_modules/emukit b/node_modules/emukit
new file mode 160000
index 0000000000000000000000000000000000000000..e02cc19ffebf0d87cde8c7fe6e37eb5c1813baa0
--- /dev/null
+++ b/node_modules/emukit
@@ -0,0 +1 @@
+Subproject commit e02cc19ffebf0d87cde8c7fe6e37eb5c1813baa0
diff --git a/src/gb.rs b/src/gb.rs
index fc7ebdd00b1e3bfc6d45a29087c037636157b67c..29c29bf0a271760f371db97b8855b0e75928070d 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -1,6 +1,7 @@
 use crate::{
     cpu::Cpu,
     data::{BootRom, CGB_BOOT, DMG_BOOT, DMG_BOOTIX, MGB_BOOTIX, SGB_BOOT},
+    gen::{COMPILATION_DATE, COMPILATION_TIME, COMPILER, COMPILER_VERSION},
     mmu::Mmu,
     pad::{Pad, PadKey},
     ppu::{Ppu, PpuMode, Tile, FRAME_BUFFER_SIZE},
@@ -194,6 +195,25 @@ impl GameBoy {
         let tile = self.get_tile(index);
         tile.palette_buffer(self.ppu().palette())
     }
+
+    /// Obtains the name of the compiler that has been
+    /// used in the compilation of the base Boytacean
+    /// library. Can be used for diagnostics.
+    pub fn get_compiler(&self) -> String {
+        String::from(COMPILER)
+    }
+
+    pub fn get_compiler_version(&self) -> String {
+        String::from(COMPILER_VERSION)
+    }
+
+    pub fn get_compilation_date(&self) -> String {
+        String::from(COMPILATION_DATE)
+    }
+
+    pub fn get_compilation_time(&self) -> String {
+        String::from(COMPILATION_TIME)
+    }
 }
 
 /// Gameboy implementations that are meant with performance
diff --git a/src/lib.rs b/src/lib.rs
index fbcf0572db10053930ca0f5d135b533d8569a007..0bc2f37c3e2d1185f3ecec18c9d1d306b39da464 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,6 +3,7 @@
 pub mod cpu;
 pub mod data;
 pub mod gb;
+pub mod gen;
 pub mod inst;
 pub mod macros;
 pub mod mmu;