diff --git a/assets/images/logos/logo.svg b/assets/images/logos/logo.svg
new file mode 100644
index 0000000..d1da010
--- /dev/null
+++ b/assets/images/logos/logo.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/assets/images/logos/spinner.svg b/assets/images/logos/spinner.svg
new file mode 100644
index 0000000..84f61ec
--- /dev/null
+++ b/assets/images/logos/spinner.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/noise/space_fine.png b/assets/images/noise/space_fine.png
new file mode 100644
index 0000000..2a69e76
Binary files /dev/null and b/assets/images/noise/space_fine.png differ
diff --git a/assets/images/noise/space_sparse.png b/assets/images/noise/space_sparse.png
new file mode 100644
index 0000000..dae9af8
Binary files /dev/null and b/assets/images/noise/space_sparse.png differ
diff --git a/assets/sounds/music/title.opus b/assets/sounds/music/title.opus
new file mode 100644
index 0000000..cbee681
Binary files /dev/null and b/assets/sounds/music/title.opus differ
diff --git a/package.json b/package.json
index 0fed3ac..2c7d5af 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,16 @@
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^2.2.0",
+ "@reduxjs/toolkit": "^1.4.0",
+ "@types/react": "^16.9.50",
+ "@types/react-dom": "^16.9.8",
+ "@types/react-redux": "^7.1.9",
"parcel-bundler": "^1.12.4",
+ "pixi.js": "^5.3.3",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-redux": "^7.2.1",
+ "redux": "^4.0.5",
"sass": "^1.26.11",
"typescript": "^4.0.3"
},
diff --git a/src/graphics/filters/InGameSpace.ts b/src/graphics/filters/InGameSpace.ts
new file mode 100644
index 0000000..3edb86e
--- /dev/null
+++ b/src/graphics/filters/InGameSpace.ts
@@ -0,0 +1,49 @@
+import { Filter, Renderer, Texture, WRAP_MODES } from "pixi.js";
+import RTTShader from "../utils/RTTShader";
+
+//@ts-expect-error Frag file
+import filter_src from "./frag/InGameSpace.frag";
+
+export interface SpaceTextures {
+ noise_sparse;
+ noise_fine;
+}
+
+export default class InGameSpaceFilter extends RTTShader {
+ private elapsed: number;
+ private filter: Filter;
+
+ constructor(renderer: Renderer, scale: number, textures: SpaceTextures) {
+ const noise_sparse = textures.noise_sparse.texture as Texture;
+ const noise_fine = textures.noise_fine.texture as Texture;
+
+ noise_sparse.baseTexture.wrapMode = WRAP_MODES.REPEAT;
+ noise_fine.baseTexture.wrapMode = WRAP_MODES.REPEAT;
+
+ const filter = new Filter(null, filter_src, {
+ time: 0,
+ ires: [renderer.width / scale, renderer.height / scale],
+ noise_sparse,
+ noise_fine,
+ scroll_speed: 0,
+ warp_boost: 0,
+ warp_opacity: 0,
+ });
+ super(renderer, filter, scale);
+ this.elapsed = 0;
+ this.filter = filter;
+ }
+
+ rescale(scale: number) {
+ super.rescale(scale);
+ this.filter.uniforms.ires = [
+ this.renderer.width / scale,
+ this.renderer.height / scale,
+ ];
+ }
+
+ update(delta: number) {
+ this.filter.uniforms.time = this.elapsed;
+ this.elapsed += delta / 60;
+ }
+}
diff --git a/src/graphics/filters/MenuBackground.ts b/src/graphics/filters/MenuBackground.ts
new file mode 100644
index 0000000..efc1da0
--- /dev/null
+++ b/src/graphics/filters/MenuBackground.ts
@@ -0,0 +1,33 @@
+import { Filter, Renderer } from "pixi.js";
+import RTTShader from "../utils/RTTShader";
+
+//@ts-expect-error Frag file
+import filter_src from "./frag/MenuBackground.frag";
+
+export default class MenuBackgroundFilter extends RTTShader {
+ private elapsed: number;
+ private filter: Filter;
+
+ constructor(renderer: Renderer, scale: number) {
+ const filter = new Filter(null, filter_src, {
+ iTime: 0,
+ iResolution: [renderer.width / scale, renderer.height / scale],
+ });
+ super(renderer, filter, scale);
+ this.elapsed = 0;
+ this.filter = filter;
+ }
+
+ rescale(scale: number) {
+ super.rescale(scale);
+ this.filter.uniforms.iResolution = [
+ this.renderer.width / scale,
+ this.renderer.height / scale,
+ ];
+ }
+
+ update(delta: number) {
+ this.filter.uniforms.iTime = this.elapsed;
+ this.elapsed += delta / 60;
+ }
+}
diff --git a/src/graphics/filters/frag/InGameSpace.frag b/src/graphics/filters/frag/InGameSpace.frag
new file mode 100644
index 0000000..a5efdd3
--- /dev/null
+++ b/src/graphics/filters/frag/InGameSpace.frag
@@ -0,0 +1,47 @@
+precision mediump float;
+
+uniform sampler2D noise_sparse;
+uniform sampler2D noise_fine;
+
+uniform vec2 ires;
+uniform float scroll_speed;
+uniform float warp_boost;
+uniform float warp_opacity;
+uniform float time;
+
+const float noise_sparse_scale = 0.0007;
+const float noise_fine_scale = 0.004;
+const float warp_clouds_scale = 0.0003;
+const vec2 scroll_dir = vec2(1,0);
+const vec2 blink_scroll = vec2(50.0, 0);
+const float PI = 3.1415;
+
+void main() {
+ vec2 uv=gl_FragCoord.xy/ires.xy;
+ float scroll = scroll_speed * time;
+ float sparse = texture2D(noise_sparse, (gl_FragCoord.xy + scroll_dir * scroll)*noise_sparse_scale).r;
+ sparse = ((sparse * sparse) - 0.2) * 1.2;
+ float fine = texture2D(noise_fine, (gl_FragCoord.xy + scroll_dir * scroll)*noise_fine_scale).r;
+ fine = fine - 0.62;
+ if (fine > 0.) {
+ fine = fine * 10.0;
+ }
+ if (fine > 0.3) {
+ float fine_blink_fade = texture2D(noise_sparse, (gl_FragCoord.xy + blink_scroll*time)*noise_sparse_scale).r;
+ fine = fine - ((fine_blink_fade * fine_blink_fade) - 0.2) * 1.2;
+ }
+ vec4 clouds = vec4(vec3(0.3, 0.5, 0.7) * sparse, 1.0);
+ vec4 stars = vec4(fine);
+
+ vec2 warp_scroll = scroll_dir * scroll;
+ vec2 warp_uv = (gl_FragCoord.xy * sin(uv.y * PI) + warp_scroll)*warp_clouds_scale;
+ vec2 warp_uv_inv = (gl_FragCoord.xy * sin((abs(uv.y-0.5)) * uv.x * PI) + warp_scroll*0.5)*warp_clouds_scale;
+ float warp_clouds_sparse = texture2D(noise_sparse, vec2(0.5,1) * warp_uv).r;
+ float warp_clouds_fine = texture2D(noise_sparse, vec2(2,1) * warp_uv_inv).r;
+
+ vec4 warp = vec4(vec3(0), 1);
+ warp += vec4(vec3(0.3,0.2,0.9)*warp_clouds_sparse, 0);
+ warp += vec4(vec3(0.5,0.0,0.5)*(warp_clouds_fine*0.5+warp_clouds_sparse)*abs(uv.y-0.5), 0);
+
+ gl_FragColor = mix(clouds + stars, warp*warp_boost, warp_opacity);
+}
\ No newline at end of file
diff --git a/src/graphics/filters/frag/MenuBackground.frag b/src/graphics/filters/frag/MenuBackground.frag
new file mode 100644
index 0000000..d80e8ab
--- /dev/null
+++ b/src/graphics/filters/frag/MenuBackground.frag
@@ -0,0 +1,69 @@
+// Star Nest by Pablo Roman Andrioli
+// This content is under the MIT License.
+precision mediump float;
+
+uniform vec2 iResolution;
+uniform float iTime;
+
+#define iterations 17
+#define formuparam 0.53
+
+#define volsteps 20
+#define stepsize 0.1
+
+#define zoom 0.800
+#define tile 0.850
+#define speed 0.001
+
+#define brightness 0.0015
+#define darkmatter 0.300
+#define distfading 0.730
+#define saturation 0.850
+
+#define rotx 0.
+#define roty 0.001
+
+void main()
+{
+ //get coords and direction
+ vec2 uv=gl_FragCoord.xy/iResolution.xy-.5;
+ uv.y*=iResolution.y/iResolution.x;
+ vec3 dir=vec3(uv*zoom,1.);
+ float time=iTime*speed+.25;
+
+ float a1=.5+iTime*rotx;
+ float a2=.8+iTime*roty;
+ mat2 rot1=mat2(vec2(cos(a1),sin(a1)),vec2(-sin(a1),cos(a1)));
+ mat2 rot2=mat2(vec2(cos(a2),sin(a2)),vec2(-sin(a2),cos(a2)));
+ dir.xz*=rot1;
+ dir.xy*=rot2;
+ vec3 from=vec3(1.,.5,0.5);
+ from+=vec3(time*2.,time,-2.);
+ from.xz*=rot1;
+ from.xy*=rot2;
+
+ //volumetric rendering
+ float s=0.1,fade=1.;
+ vec3 v=vec3(0.);
+ for (int r=0; r6) fade*=1.-dm; // dark matter, don't render near
+ //v+=vec3(dm,dm*.5,0.);
+ v+=fade;
+ v+=vec3(s,s*s,s*s*s*s)*a*brightness*fade; // coloring based on distance
+ fade*=distfading; // distance fading
+ s+=stepsize;
+ }
+ v=mix(vec3(length(v)),v,saturation); //color adjust
+ gl_FragColor = vec4(v*.01,1.);
+
+}
\ No newline at end of file
diff --git a/src/graphics/utils/RTTShader.ts b/src/graphics/utils/RTTShader.ts
new file mode 100644
index 0000000..8a32859
--- /dev/null
+++ b/src/graphics/utils/RTTShader.ts
@@ -0,0 +1,30 @@
+import { RenderTexture, Container, Renderer, Filter } from "pixi.js";
+
+export default class RTTShader {
+ readonly texture: RenderTexture;
+ readonly container: Container;
+ readonly renderer: Renderer;
+
+ constructor(renderer: Renderer, filter: Filter, scale: number = 2) {
+ this.renderer = renderer;
+ this.texture = RenderTexture.create({
+ width: this.renderer.width / scale,
+ height: this.renderer.height / scale,
+ });
+ this.container = new Container();
+ this.container.filterArea = this.texture.frame;
+ this.container.filters = [filter];
+ }
+
+ rescale(scale: number) {
+ this.texture.resize(
+ this.renderer.width / scale,
+ this.renderer.height / scale
+ );
+ this.container.filterArea = this.texture.frame;
+ }
+
+ render() {
+ this.renderer.render(this.container, this.texture);
+ }
+}
diff --git a/src/index.html b/src/index.html
index 85cc5af..3fe391a 100644
--- a/src/index.html
+++ b/src/index.html
@@ -7,6 +7,8 @@
+
+