many many changes

This commit is contained in:
elijah 2024-06-05 21:53:45 +02:00
parent 0a00663314
commit 45c94224ba
16 changed files with 135 additions and 1222 deletions

View File

@ -1,10 +1,44 @@
window.addEventListener('load', function() {
if (!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
fetch('assets/ascii.txt')
.then(response => response.text())
.then(data => {
const asciiArtContainer = document.getElementById('ascii-art-container');
asciiArtContainer.innerText = data;
});
fetch('assets/ascii.txt')
.then(response => response.text())
.then(data => {
const asciiArtContainer = document.getElementById('ascii-art-container');
asciiArtContainer.innerText = data;
initAsciiEffect(data);
});
});
function adjustFontSize() {
const asciiArtContainer = document.getElementById('ascii-art-container');
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const baseFontSize = Math.min(windowWidth, windowHeight) / 150;
asciiArtContainer.style.fontSize = `${baseFontSize}px`;
asciiArtContainer.style.top = `${windowHeight / 2}px`;
asciiArtContainer.style.left = `${windowWidth / 2}px`;
asciiArtContainer.style.transform = 'translate(-50%, -50%)';
}
window.addEventListener('resize', adjustFontSize);
window.addEventListener('load', adjustFontSize);
function initAsciiEffect(asciiArt) {
const asciiArtContainer = document.getElementById('ascii-art-container');
const chars = asciiArt.split('');
const updateFrequency = 1;
function getRandomIndex() {
return Math.floor(Math.random() * chars.length);
}
});
function flipBit() {
const index = getRandomIndex();
const char = chars[index];
if (char === '0') {
chars[index] = '1';
} else if (char === '1') {
chars[index] = '0';
} else if (char === '@') {
chars[index] = '#';
} else if (char === '#') {
chars[index] = '@';
}
asciiArtContainer.innerText = chars.join('');
}
setInterval(flipBit, updateFrequency);
}

View File

@ -2,7 +2,6 @@
font-family: 'workbench';
src: url('../assets/font.woff2') format('woff2');
}
body {
background-color: #333;
background-image:
@ -14,7 +13,6 @@ body {
background-size: 100% 3px;
background-repeat: repeat-y;
}
.navbar {
background:
radial-gradient(
@ -30,14 +28,12 @@ body {
justify-content: space-between;
align-items: center;
}
.logo {
font-family: 'workbench', sans-serif;
font-size: 1.5rem;
color: #33ccffca;
text-shadow: 0 0 10px #33ccffbe;
}
.nav-links {
list-style: none;
margin: 0;
@ -45,20 +41,17 @@ body {
display: flex;
align-items: center;
}
.nav-links li {
margin-right: 20px;
}
.nav-links a {
font-family: 'workbench', sans-serif;
font-size: 1.2rem;
color: #33ccffab; /* a pale blue color reminiscent of old CRTs */
color: #33ccffab;
text-decoration: none;
transition: color 0.2s ease;
text-shadow: 0 0 3px #33ccffaf;
}
.nav-links a:hover {
color: #33ccffda;
}
@ -67,20 +60,28 @@ body {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: calc(5px + 0.005vw); /* Dynamically adjust the font size based on the viewport width */
font-size: 10vmin;
font-family: 'Courier New', monospace;
color: #33ccffab;
text-shadow: 0 0 3px #33ccffaf;
padding: 10px;
white-space: pre;
pointer-events: none;
max-height: calc(100vh - 80px); /* Subtract the max possible navbar height to restrict container size */
overflow: auto; /* Add scrollbar to container if content exceeds its max-height */
max-width: 90%;
max-height: 90%;
overflow: hidden;
}
@media only screen and (max-width: 768px) {
.ascii-art-container {
display: none;
@media (max-width: 600px) {
.navbar {
flex-direction: column;
align-items: center;
}
}
.nav-links {
flex-direction: column;
align-items: center;
}
.nav-links li {
margin: 5px 0;
}
}

View File

@ -1,117 +0,0 @@
const init = () => {
const renderer = new THREE.WebGLRenderer({ antialias:true });
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const orthographiCamera = new THREE.OrthographicCamera(window.innerWidth / -2.0, window.innerWidth / +2.0, window.innerHeight / +2.0, window.innerHeight / -2.0, 0.0, 1.0);
const perspectiveCamera = new THREE.PerspectiveCamera(45.0, window.innerWidth / window.innerHeight, 0.1, 1000.0);
const controls = new THREE.OrbitControls(perspectiveCamera, renderer.domElement);
const clock = new THREE.Clock();
perspectiveCamera.position.set(0.0, 0.0, 5.0);
perspectiveCamera.lookAt(new THREE.Vector3(0.0, 0.0, 0.0));
const geometry = new THREE.PlaneBufferGeometry(window.innerWidth, window.innerHeight);
const uniforms = {
uApp: {
value: {
time: clock.getElapsedTime(),
resolution: new THREE.Vector2(window.innerWidth, window.innerHeight)
}
},
uCamera: {
value: {
position: perspectiveCamera.position,
viewMatrix: perspectiveCamera.matrixWorldInverse,
projectionMatrix: perspectiveCamera.projectionMatrix
}
},
uParams: {
value: {
numIterations: 30,
convergenceCriteria: 0.0001,
finiteDifferenceEpsilon: 0.0001
}
},
uScene: {
value: {
backgroundColor: new THREE.Vector3(0.0, 0.0, 0.0),
lights: [
{
direction: new THREE.Vector3(1.0, 1.0, 1.0),
ambientColor: new THREE.Vector3(1.0, 1.0, 1.0),
diffuseColor: new THREE.Vector3(1.0, 1.0, 0.0),
specularColor: new THREE.Vector3(1.0, 1.0, 0.0)
},
{
direction: new THREE.Vector3(-1.0, -1.0, -1.0),
ambientColor: new THREE.Vector3(1.0, 1.0, 1.0),
diffuseColor: new THREE.Vector3(1.0, 0.0, 1.0),
specularColor: new THREE.Vector3(1.0, 0.0, 1.0)
}
],
material: {
ambientColor: new THREE.Vector3(0.05, 0.05, 0.05),
diffuseColor: new THREE.Vector3(0.5, 0.5, 0.5),
specularColor: new THREE.Vector3(1.0, 1.0, 1.0),
emissionColor: new THREE.Vector3(0.0, 0.0, 0.0),
shininess: 64.0
},
bound: {
position: new THREE.Vector3(0.0, 0.0, 0.0),
radius: 2.0
},
fractal: {
power: 10,
numIterations: 4,
escapeCriteria: 2.0
}
}
}
}
const material = new THREE.ShaderMaterial({
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
uniforms: uniforms
});
scene.add(new THREE.Mesh(geometry, material));
const onWindowResize = (event) => {
uniforms.uApp.value.resolution.x = window.innerWidth * window.devicePixelRatio;
uniforms.uApp.value.resolution.y = window.innerHeight * window.devicePixelRatio;
// NOTE: https://ics.media/tutorial-three/renderer_resize/
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
perspectiveCamera.aspect = window.innerWidth / window.innerHeight;
perspectiveCamera.updateProjectionMatrix();
}
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
const animate = () => {
requestAnimationFrame(animate);
const update = () => {
controls.update();
perspectiveCamera.lookAt(new THREE.Vector3(0.0, 0.0, 0.0));
uniforms.uApp.value.time = clock.getElapsedTime();
uniforms.uCamera.value.position = perspectiveCamera.position;
uniforms.uCamera.value.viewMatrix = perspectiveCamera.matrixWorldInverse;
uniforms.uCamera.value.projectionMatrix = perspectiveCamera.projectionMatrix;
}
update();
renderer.render(scene, orthographiCamera);
};
animate();
}
window.addEventListener("load", init);

View File

@ -1,889 +0,0 @@
<script id="vertexShader" type="x-shader/x-vertex">
void main(){
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
#define SDF_DERIVATIVE_TYPE 0
#define NORMAL_DERIVATIVE_TYPE 0
// ================ variables ================ //
// ---------------- application ---------------- //
struct App
{
float time;
vec2 resolution;
};
struct Camera
{
vec3 position;
mat4 viewMatrix;
mat4 projectionMatrix;
};
struct Params
{
int numIterations;
float convergenceCriteria;
float finiteDifferenceEpsilon;
};
// ---------------- lighting ---------------- //
struct PointLight
{
vec3 position;
vec3 ambientColor;
vec3 diffuseColor;
vec3 specularColor;
};
struct DirectionalLight
{
vec3 direction;
vec3 ambientColor;
vec3 diffuseColor;
vec3 specularColor;
};
struct Material
{
vec3 ambientColor;
vec3 diffuseColor;
vec3 specularColor;
vec3 emissionColor;
float shininess;
};
// ---------------- primitives ---------------- //
struct Line
{
vec3 position;
vec3 direction;
};
struct Sphere
{
vec3 position;
float radius;
};
struct Intersection
{
vec3 position;
bool intersected;
};
// ---------------- scene ---------------- //
const int numLights = 2;
struct Fractal
{
int power;
int numIterations;
float escapeCriteria;
};
struct Scene
{
vec3 backgroundColor;
DirectionalLight lights[numLights];
Material material;
Sphere bound;
Fractal fractal;
};
// ---------------- uniform ---------------- //
uniform App uApp;
uniform Camera uCamera;
uniform Params uParams;
uniform Scene uScene;
// ================ functions ================ //
// ---------------- utilities ---------------- //
vec2 linmap(vec2 in_val, vec2 in_min, vec2 in_max, vec2 out_min, vec2 out_max)
{
return (in_val - in_min) / (in_max - in_min) * (out_max - out_min) + out_min;
}
// ---------------- primitives ---------------- //
float sdfSphere(Sphere sphere, vec3 position)
{
return length(position - sphere.position) - sphere.radius;
}
vec3 normalSphere(Sphere sphere, vec3 position)
{
return normalize(position - sphere.position);
}
Intersection intersectionSphereLine(Sphere sphere, Line line)
{
vec3 difference = line.position - sphere.position;
float a = dot(line.direction, line.direction);
float b = 2.0 * dot(difference, line.direction);
float c = dot(difference, difference) - pow(sphere.radius, 2.0);
float d = pow(b, 2.0) - 4.0 * a * c;
float t = (-b - sqrt(d)) / (2.0 * a);
return Intersection(line.position + t * line.direction, d >= 0.0);
}
// ---------------- constructive solid geometry ---------------- //
float csgUnion(float sd1, float sd2) { return min(sd1, sd2); }
float csgSubtraction(float sd1, float sd2) { return max(-sd1, sd2); }
float csgIntersection(float sd1, float sd2) { return max(sd1, sd2); }
// ---------------- complex ---------------- //
vec2 cAdd(vec2 c1, vec2 c2)
{
// return vec2(c1.x + c2.x, c1.y + c2.y);
return c1 + c2;
}
vec2 cSub(vec2 c1, vec2 c2)
{
// return vec2(c1.x - c2.x, c1.y - c2.y);
return c1 - c2;
}
vec2 cMul(vec2 c1, vec2 c2)
{
return vec2(c1.x * c2.x - c1.y * c2.y, c1.y * c2.x + c1.x * c2.y);
}
vec2 cConj(vec2 c)
{
return vec2(c.x, -c.y);
}
float cNorm(vec2 c)
{
// return sqrt(cMul(c, cConj(c)).x);
return length(c);
}
vec2 cInv(vec2 c)
{
return cConj(c) / pow(cNorm(c), 2.0);
}
vec2 cDiv(vec2 c1, vec2 c2)
{
return cMul(c1, cInv(c2));
}
vec2 cPow(vec2 c, int n)
{
vec2 p = vec2(1.0, 0.0);
for (int i = 0; i < n; ++i)
{
p = cMul(p, c);
}
return p;
}
// ---------------- quaternion ---------------- //
vec4 qAdd(vec4 q1, vec4 q2)
{
// return vec4(q1.x + q2.x, q1.yzw + q2.yzw);
return q1 + q2;
}
vec4 qSub(vec4 q1, vec4 q2)
{
// return vec4(q1.x - q2.x, q1.yzw - q2.yzw);
return q1 - q2;
}
vec4 qMul(vec4 q1, vec4 q2)
{
return vec4(q1.x * q2.x - dot(q1.yzw, q2.yzw), q2.x * q1.yzw + q1.x * q2.yzw + cross(q1.yzw, q2.yzw));
}
vec4 qConj(vec4 q)
{
return vec4(q.x, -q.yzw);
}
float qNorm(vec4 q)
{
// return sqrt(qMul(q, qConj(q)).x);
return length(q);
}
vec4 qInv(vec4 q)
{
return qConj(q) / pow(qNorm(q), 2.0);
}
vec4 qDiv(vec4 q1, vec4 q2)
{
return qMul(q1, qInv(q2));
}
vec4 qPow(vec4 q, int n)
{
vec4 p = vec4(1.0, vec3(0.0));
for (int i = 0; i < n; ++i)
{
p = qMul(p, q);
}
return p;
}
// ---------------- trinion ---------------- //
vec3 tAdd(vec3 t1, vec3 t2)
{
return t1 + t2;
}
vec3 tSub(vec3 t1, vec3 t2)
{
return t1 - t2;
}
vec3 tMul(vec3 t1, vec3 t2)
{
float r1 = length(t1);
float r2 = length(t2);
if (r1 > 0.0 && r2 > 0.0)
{
float a1 = asin(t1.z / r1);
float a2 = asin(t2.z / r2);
float b1 = atan(t1.y, t1.x);
float b2 = atan(t2.y, t2.x);
float r = r1 * r2;
float a = a1 + a2;
float b = b1 + b2;
float x = r * cos(a) * cos(b);
float y = r * cos(a) * sin(b);
float z = r * sin(a);
return vec3(x, y, z);
}
else
{
return vec3(0.0);
}
}
vec3 tPow(vec3 t, int n)
{
/* NOTE: This does not work
vec3 p = vec3(1.0, vec2(0.0));
for (int i = 0; i < n; ++i)
{
p = tMul(p, t);
}
return p;
*/
float r = length(t);
if (r > 0.0)
{
float a = asin(t.z / r);
float b = atan(t.y, t.x);
float pr = pow(r, float(n));
float pa = a * float(n);
float pb = b * float(n);
float x = pr * cos(pa) * cos(pb);
float y = pr * cos(pa) * sin(pb);
float z = pr * sin(pa);
return vec3(x, y, z);
}
else
{
return vec3(0.0);
}
}
// ---------------- dual ---------------- //
struct DualQ
{
vec4 q;
vec4 d;
};
DualQ dqAdd(DualQ dq1, DualQ dq2)
{
return DualQ(qAdd(dq1.q, dq2.q), qAdd(dq1.d, dq2.d));
}
DualQ dqSub(DualQ dq1, DualQ dq2)
{
return DualQ(qSub(dq1.q, dq2.q), qSub(dq1.d, dq2.d));
}
DualQ dqMul(DualQ dq1, DualQ dq2)
{
return DualQ(qMul(dq1.q, dq2.q), qAdd(qMul(dq1.d, dq2.q), qMul(dq1.q, dq2.d)));
}
DualQ dqDiv(DualQ dq1, DualQ dq2)
{
return DualQ(qDiv(dq1.q, dq2.q), qDiv(qSub(qMul(dq1.d, dq2.q), qMul(dq1.q, dq2.d)), qMul(dq2.q, dq2.q)));
}
DualQ dqPow(DualQ dq, int n)
{
DualQ dp = DualQ(vec4(1.0, vec3(0.0)), vec4(0.0, vec3(0.0)));
for (int i = 0; i < n; ++i)
{
dp = dqMul(dp, dq);
}
return dp;
}
struct DualS
{
float s;
float d;
};
DualS dAdd(DualS d1, DualS d2)
{
return DualS(d1.s + d2.s, d1.d + d2.d);
}
DualS dSub(DualS d1, DualS d2)
{
return DualS(d1.s - d2.s, d1.d - d2.d);
}
DualS dMul(DualS d1, DualS d2)
{
return DualS(d1.s * d2.s, d1.d * d2.s + d1.s * d2.d);
}
DualS dDiv(DualS d1, DualS d2)
{
return DualS(d1.s / d2.s, (d1.d * d2.s - d1.s * d2.d) / (d2.s * d2.s));
}
DualS dPow(DualS d, float n)
{
return DualS(pow(d.s, n), d.d * n * pow(d.s, n - 1.0));
}
DualS dSqrt(DualS d)
{
return DualS(sqrt(d.s), d.d * 0.5 / sqrt(d.s));
}
DualS dSin(DualS d)
{
return DualS(sin(d.s), d.d * cos(d.s));
}
DualS dCos(DualS d)
{
return DualS(cos(d.s), d.d * -sin(d.s));
}
DualS dTan(DualS d)
{
return DualS(tan(d.s), d.d / pow(cos(d.s), 2.0));
}
DualS dArcSin(DualS d)
{
return DualS(asin(d.s), d.d / sqrt(1.0 - pow(d.s, 2.0)));
}
DualS dArcCos(DualS d)
{
return DualS(acos(d.s), d.d / -sqrt(1.0 - pow(d.s, 2.0)));
}
DualS dArcTan(DualS d)
{
return DualS(atan(d.s), d.d / (1.0 + pow(d.s, 2.0)));
}
struct DualT
{
vec3 t;
vec3 d;
};
DualT dtAdd(DualT dt1, DualT dt2)
{
return DualT(dt1.t + dt2.t, dt1.d + dt2.d);
}
DualT dtSub(DualT dt1, DualT dt2)
{
return DualT(dt1.t - dt2.t, dt1.d - dt2.d);
}
DualT dtMul(DualT dt1, DualT dt2)
{
// return DualT(tMul(dt1.t, dt2.t), tAdd(tMul(dt1.d, dt2.t), tMul(dt1.t, dt2.d)));
DualS dx1 = DualS(dt1.t.x, dt1.d.x);
DualS dy1 = DualS(dt1.t.y, dt1.d.y);
DualS dz1 = DualS(dt1.t.z, dt1.d.z);
DualS dx2 = DualS(dt2.t.x, dt2.d.x);
DualS dy2 = DualS(dt2.t.y, dt2.d.y);
DualS dz2 = DualS(dt2.t.z, dt2.d.z);
DualS dr1 = dSqrt(dAdd(dPow(dx1, 2.0), dAdd(dPow(dy1, 2.0), dPow(dz1, 2.0))));
DualS dr2 = dSqrt(dAdd(dPow(dx2, 2.0), dAdd(dPow(dy2, 2.0), dPow(dz2, 2.0))));
if (dr1.s > 0.0 && dr2.s > 0.0)
{
DualS da1 = dArcSin(dDiv(dz1, dr1));
DualS da2 = dArcSin(dDiv(dz2, dr2));
DualS db1 = dArcTan(dDiv(dy1, dx1));
DualS db2 = dArcTan(dDiv(dy2, dx2));
DualS dr = dMul(dr1, dr2);
DualS da = dAdd(da1, da2);
DualS db = dAdd(db1, db2);
DualS dx = dMul(dr, dMul(dCos(da), dCos(db)));
DualS dy = dMul(dr, dMul(dCos(da), dSin(db)));
DualS dz = dMul(dr, dSin(da));
return DualT(vec3(dx.s, dy.s, dz.s), vec3(dx.d, dy.d, dz.d));
}
else
{
return DualT(vec3(0.0), vec3(0.0));
}
}
DualT dtPow(DualT dt, int n)
{
/* NOTE: This does not work
DualT dp = DualT(vec3(1.0, vec2(0.0)), vec3(0.0, vec2(0.0)));
for (int i = 0; i < n; ++i)
{
dp = dtMul(dp, dt);
}
return dp;
*/
DualS dx = DualS(dt.t.x, dt.d.x);
DualS dy = DualS(dt.t.y, dt.d.y);
DualS dz = DualS(dt.t.z, dt.d.z);
DualS dr = dSqrt(dAdd(dPow(dx, 2.0), dAdd(dPow(dy, 2.0), dPow(dz, 2.0))));
if (dr.s > 0.0)
{
DualS da = dArcSin(dDiv(dz, dr));
DualS db = dArcTan(dDiv(dy, dx));
DualS dpr = dPow(dr, float(n));
DualS dpa = dMul(da, DualS(float(n), 0.0));
DualS dpb = dMul(db, DualS(float(n), 0.0));
DualS dx = dMul(dpr, dMul(dCos(dpa), dCos(dpb)));
DualS dy = dMul(dpr, dMul(dCos(dpa), dSin(dpb)));
DualS dz = dMul(dpr, dSin(dpa));
return DualT(vec3(dx.s, dy.s, dz.s), vec3(dx.d, dy.d, dz.d));
}
else
{
return DualT(vec3(0.0), vec3(0.0));
}
}
// ---------------- fractals ---------------- //
#if SDF_DERIVATIVE_TYPE == 0
float sdfJulia(Fractal fractal, vec4 z, vec4 c)
{
DualQ dzx = DualQ(z, vec4(1.0, 0.0, 0.0, 0.0));
DualQ dzy = DualQ(z, vec4(0.0, 1.0, 0.0, 0.0));
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 1.0, 0.0));
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 1.0));
DualQ dcx = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcy = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
for (int i = 0; i < fractal.numIterations; ++i)
{
// forward-mode automatic differentiation
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
return (qNorm(dzx.q) * log(qNorm(dzx.q))) / (2.0 * qNorm(normalize(dzx.q) * J));
}
float sdfMandelbrot(Fractal fractal, vec4 c, vec4 z)
{
DualQ dzx = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dzy = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcx = DualQ(c, vec4(1.0, 0.0, 0.0, 0.0));
DualQ dcy = DualQ(c, vec4(0.0, 1.0, 0.0, 0.0));
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 1.0, 0.0));
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 1.0));
for (int i = 0; i < fractal.numIterations; ++i)
{
// forward-mode automatic differentiation
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
return (qNorm(dzx.q) * log(qNorm(dzx.q))) / (2.0 * qNorm(normalize(dzx.q) * J));
}
float sdfMandelbulb(Fractal fractal, vec3 c, vec3 z)
{
DualT dzx = DualT(z, vec3(0.0, 0.0, 0.0));
DualT dzy = DualT(z, vec3(0.0, 0.0, 0.0));
DualT dzz = DualT(z, vec3(0.0, 0.0, 0.0));
DualT dcx = DualT(c, vec3(1.0, 0.0, 0.0));
DualT dcy = DualT(c, vec3(0.0, 1.0, 0.0));
DualT dcz = DualT(c, vec3(0.0, 0.0, 1.0));
for (int i = 0; i < fractal.numIterations; ++i)
{
// forward-mode automatic differentiation
dzx = dtAdd(dtPow(dzx, fractal.power), dcx);
dzy = dtAdd(dtPow(dzy, fractal.power), dcy);
dzz = dtAdd(dtPow(dzz, fractal.power), dcz);
if (length(dzx.t) > fractal.escapeCriteria) break;
}
mat3 J = mat3(dzx.d, dzy.d, dzz.d);
return (length(dzx.t) * log(length(dzx.t))) / (2.0 * length(normalize(dzx.t) * J));
}
#elif SDF_DERIVATIVE_TYPE == 1
float sdfJulia(Fractal fractal, vec4 z, vec4 c)
{
vec4 dzx = vec4(1.0, 0.0, 0.0, 0.0);
vec4 dzy = vec4(0.0, 1.0, 0.0, 0.0);
vec4 dzz = vec4(0.0, 0.0, 1.0, 0.0);
vec4 dzw = vec4(0.0, 0.0, 0.0, 1.0);
vec4 dcx = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcy = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcz = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcw = vec4(0.0, 0.0, 0.0, 0.0);
for (int i = 0; i < fractal.numIterations; ++i)
{
vec4 zp = qPow(z, fractal.power - 1);
// forward-mode manual differentiation
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
z = qAdd(qMul(zp, z), c);
if (qNorm(z) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx, dzy, dzz, dzw);
return (qNorm(z) * log(qNorm(z))) / (2.0 * qNorm(normalize(z) * J));
}
float sdfMandelbrot(Fractal fractal, vec4 c, vec4 z)
{
vec4 dzx = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dzy = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dzz = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dzw = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcx = vec4(1.0, 0.0, 0.0, 0.0);
vec4 dcy = vec4(0.0, 1.0, 0.0, 0.0);
vec4 dcz = vec4(0.0, 0.0, 1.0, 0.0);
vec4 dcw = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < fractal.numIterations; ++i)
{
vec4 zp = qPow(z, fractal.power - 1);
// forward-mode manual differentiation
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
z = qAdd(qMul(zp, z), c);
if (qNorm(z) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx, dzy, dzz, dzw);
return (qNorm(z) * log(qNorm(z))) / (2.0 * qNorm(normalize(z) * J));
}
#else
#endif
#if NORMAL_DERIVATIVE_TYPE == 0
vec4 normalJulia(Fractal fractal, vec4 z, vec4 c)
{
DualQ dzx = DualQ(z, vec4(1.0, 0.0, 0.0, 0.0));
DualQ dzy = DualQ(z, vec4(0.0, 1.0, 0.0, 0.0));
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 1.0, 0.0));
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 1.0));
DualQ dcx = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcy = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 0.0));
for (int i = 0; i < fractal.numIterations; ++i)
{
// forward-mode automatic differentiation
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
return dzx.q * J;
}
vec4 normalMandelbrot(Fractal fractal, vec4 c, vec4 z)
{
DualQ dzx = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dzy = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dzz = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dzw = DualQ(z, vec4(0.0, 0.0, 0.0, 0.0));
DualQ dcx = DualQ(c, vec4(1.0, 0.0, 0.0, 0.0));
DualQ dcy = DualQ(c, vec4(0.0, 1.0, 0.0, 0.0));
DualQ dcz = DualQ(c, vec4(0.0, 0.0, 1.0, 0.0));
DualQ dcw = DualQ(c, vec4(0.0, 0.0, 0.0, 1.0));
for (int i = 0; i < fractal.numIterations; ++i)
{
// forward-mode automatic differentiation
dzx = dqAdd(dqPow(dzx, fractal.power), dcx);
dzy = dqAdd(dqPow(dzy, fractal.power), dcy);
dzz = dqAdd(dqPow(dzz, fractal.power), dcz);
dzw = dqAdd(dqPow(dzw, fractal.power), dcw);
if (qNorm(dzx.q) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx.d, dzy.d, dzz.d, dzw.d);
return dzx.q * J;
}
vec3 normalMandelbulb(Fractal fractal, vec3 c, vec3 z)
{
DualT dzx = DualT(z, vec3(0.0, 0.0, 0.0));
DualT dzy = DualT(z, vec3(0.0, 0.0, 0.0));
DualT dzz = DualT(z, vec3(0.0, 0.0, 0.0));
DualT dcx = DualT(c, vec3(1.0, 0.0, 0.0));
DualT dcy = DualT(c, vec3(0.0, 1.0, 0.0));
DualT dcz = DualT(c, vec3(0.0, 0.0, 1.0));
for (int i = 0; i < fractal.numIterations; ++i)
{
// forward-mode automatic differentiation
dzx = dtAdd(dtPow(dzx, fractal.power), dcx);
dzy = dtAdd(dtPow(dzy, fractal.power), dcy);
dzz = dtAdd(dtPow(dzz, fractal.power), dcz);
if (length(dzx.t) > fractal.escapeCriteria) break;
}
mat3 J = mat3(dzx.d, dzy.d, dzz.d);
return dzx.t * J;
}
#elif NORMAL_DERIVATIVE_TYPE == 1
vec4 normalJulia(Fractal fractal, vec4 z, vec4 c)
{
vec4 dzx = vec4(1.0, 0.0, 0.0, 0.0);
vec4 dzy = vec4(0.0, 1.0, 0.0, 0.0);
vec4 dzz = vec4(0.0, 0.0, 1.0, 0.0);
vec4 dzw = vec4(0.0, 0.0, 0.0, 1.0);
vec4 dcx = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcy = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcz = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcw = vec4(0.0, 0.0, 0.0, 0.0);
for (int i = 0; i < fractal.numIterations; ++i)
{
vec4 zp = qPow(z, fractal.power - 1);
// forward-mode manual differentiation
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
z = qAdd(qMul(zp, z), c);
if (qNorm(z) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx, dzy, dzz, dzw);
return z * J;
}
vec4 normalMandelbrot(Fractal fractal, vec4 c, vec4 z)
{
vec4 dzx = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dzy = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dzz = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dzw = vec4(0.0, 0.0, 0.0, 0.0);
vec4 dcx = vec4(1.0, 0.0, 0.0, 0.0);
vec4 dcy = vec4(0.0, 1.0, 0.0, 0.0);
vec4 dcz = vec4(0.0, 0.0, 1.0, 0.0);
vec4 dcw = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < fractal.numIterations; ++i)
{
vec4 zp = qPow(z, fractal.power - 1);
// forward-mode manual differentiation
dzx = qAdd(float(fractal.power) * qMul(zp, dzx), dcx);
dzy = qAdd(float(fractal.power) * qMul(zp, dzy), dcy);
dzz = qAdd(float(fractal.power) * qMul(zp, dzz), dcz);
dzw = qAdd(float(fractal.power) * qMul(zp, dzw), dcw);
z = qAdd(qMul(zp, z), c);
if (qNorm(z) > fractal.escapeCriteria) break;
}
mat4 J = mat4(dzx, dzy, dzz, dzw);
return z * J;
}
#else
#endif
// ---------------- reflection ---------------- //
vec3 phongReflection(vec3 surfaceNormal, vec3 viewDirection, DirectionalLight lights[numLights], Material material)
{
surfaceNormal = normalize(surfaceNormal);
viewDirection = normalize(viewDirection);
vec3 ambientColor = vec3(0.0);
vec3 diffuseColor = vec3(0.0);
vec3 specularColor = vec3(0.0);
for (int i = 0; i < lights.length(); ++i)
{
vec3 lightDirection = normalize(lights[i].direction);
vec3 reflectedDirection = reflect(lightDirection, surfaceNormal);
float diffuseCoefficient = max(dot(-lightDirection, surfaceNormal), 0.0);
float specularCoefficient = pow(max(dot(reflectedDirection, -viewDirection), 0.0), material.shininess);
ambientColor += lights[i].ambientColor * material.ambientColor;
diffuseColor += lights[i].diffuseColor * material.diffuseColor * diffuseCoefficient;
specularColor += lights[i].specularColor * material.specularColor * specularCoefficient;
}
vec3 color = clamp(ambientColor + diffuseColor + specularColor + material.emissionColor, 0.0, 1.0);
return color;
}
// ---------------- sphere tracing ---------------- //
vec3 sphereTracing(App app, Scene scene, Params params, Line ray)
{
Intersection intersection = intersectionSphereLine(scene.bound, ray);
if (intersection.intersected)
{
ray.position = intersection.position;
// The hyperparameter from Inigo Quilez (https://www.shadertoy.com/view/MsfGRr)
vec4 juliaType = 0.45 * cos(vec4(0.5, 3.9, 1.4, 1.1) + app.time * 0.15 * vec4(1.2, 1.7, 1.3, 2.5)) - vec4(0.3, 0.0, 0.0, 0.0);
vec4 criticalPoint = vec4(0.0);
for (int i = 0; i < params.numIterations; ++i)
{
float sd = sdfMandelbulb(scene.fractal, ray.position, criticalPoint.xyz);
// ray marching
ray.position += sd * ray.direction;
// collision detection
if (abs(sd) < params.convergenceCriteria)
{
vec3 surfaceNormal = normalize(normalMandelbulb(scene.fractal, ray.position, criticalPoint.xyz));
vec3 fragColor = phongReflection(surfaceNormal, ray.direction, scene.lights, scene.material);
return fragColor;
}
if (sdfSphere(scene.bound, ray.position) > 0.0) break;
}
}
return scene.backgroundColor;
}
// ---------------- main ---------------- //
void main()
{
vec2 fragCoord = linmap(gl_FragCoord.xy, vec2(0, 0), uApp.resolution, vec2(-1.0, -1.0), vec2(1.0, 1.0));
vec3 rayDirection = normalize(inverse(mat3(uCamera.projectionMatrix) * mat3(uCamera.viewMatrix)) * vec3(fragCoord, 1.0));
Line ray = Line(uCamera.position, rayDirection);
vec3 fragColor = sphereTracing(uApp, uScene, uParams, ray);
gl_FragColor = vec4(fragColor, 1.0);
}
</script>
<script src="bulb.js"></script>
<script src="https://unpkg.com/three@0.139.2/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.139.2/examples/js/controls/OrbitControls.js"></script>

188
demo.html
View File

@ -1,188 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Mandelbulb ASCII Art</title>
<style>
body {
font-family: 'Courier New', monospace;
white-space: pre;
background: #000;
color: #0f0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
overflow: hidden;
}
#asciiArt {
margin: auto;
overflow: hidden;
font-size: 7px;
line-height: 7px;
letter-spacing: 1px;
}
.controls {
position: fixed;
top: 20px;
left: 20px;
color: #0f0;
}
input[type='number'] {
width: 60px;
background: #222;
border: 1px solid #333;
color: #0f0;
margin: 2px 0;
}
label {
display: inline-block;
width: 100px;
}
</style>
</head>
<body>
<div class="controls">
<label>Power: <input type="number" id="power" min="2" max="12" step="0.1" value="8"></label><br>
<label>Detail: <input type="number" step="0.01" id="detail" value="0.25"></label><br>
<label>Columns: <input type="number" id="columns" value="80"></label>
</div>
<pre id="asciiArt"></pre>
<script>
'use strict';
// Mandelbulb Calculation and Rendering
class Mandelbulb {
constructor(power, detail, columns, rows) {
this.power = power;
this.detail = detail;
this.columns = columns;
this.rows = rows;
this.charset = " .:░▒▓█"; // Use extended charset for ASCII view detail
}
// Normalized rotation using yaw and pitch for 3D rotation
rotate(x, y, z, sinA, cosA, sinB, cosB) {
const yr = y * cosA - z * sinA;
const zr = y * sinA + z * cosA;
const xr = x * cosB - zr * sinB;
const zrr = x * sinB + zr * cosB;
return [xr, yr, zrr];
}
// 3D Distance Estimation iterative formula for the Mandelbulb fractal
computeMandelbulbDistance(x0, y0, z0, maxIterations, escapeRadius) {
let x = x0, y = y0, z = z0, dr = 1.0;
let r = 0;
for (let i = 0; i < maxIterations; i++) {
r = Math.sqrt(x * x + y * y + z * z);
if (r > escapeRadius) break;
// Convert to polar coordinates
const theta = Math.atan2(Math.sqrt(x * x + y * y), z);
const phi = Math.atan2(y, x);
const zr = Math.pow(r, this.power - 1);
dr = Math.pow(r, this.power - 1) * this.power * dr + 1.0;
const thetaR = theta * this.power;
const phiR = phi * this.power;
// Convert back to cartesian coordinates
x = zr * Math.sin(thetaR) * Math.cos(phiR) + x0;
y = zr * Math.sin(thetaR) * Math.sin(phiR) + y0;
z = zr * Math.cos(thetaR) + z0;
}
return 0.5 * Math.log(r) * r / dr;
}
render(angleX, angleY) {
const output = Array.from({ length: this.rows }, () => []);
const maxIterations = 256;
const escapeRadius = 10;
const voxelSize = 2.5 / Math.min(this.columns, this.rows);
const fov = 1.0 / Math.tan(0.5);
const cosA = Math.cos(angleX), sinA = Math.sin(angleX);
const cosB = Math.cos(angleY), sinB = Math.sin(angleY);
const cameraZ = -2; // Camera position tweak for better fractal appearance
for (let row = 0; row < this.rows; row++) {
for (let col = 0; col < this.columns; col++) {
const px = (col - this.columns / 2) * voxelSize;
const py = (row - this.rows / 2) * voxelSize;
const pz = fov;
const [dx, dy, dz] = this.rotate(px, py, pz, sinA, cosA, sinB, cosB);
const [sx, sy, sz] = this.rotate(0, 0, cameraZ, sinA, cosA, sinB, cosB);
let depth = this.rayMarch(sx, sy, sz, dx, dy, dz, maxIterations, escapeRadius);
output[row][col] = this.depthToAscii(depth);
}
}
return output.map(line => line.join('')).join('\n');
}
// Ray marching for the fractal
rayMarch(x, y, z, dx, dy, dz, maxIterations, escapeRadius) {
let depth = 0.0;
for (let i = 0; i < maxIterations; i++) {
const distance = this.computeMandelbulbDistance(x, y, z, maxIterations, escapeRadius);
if (distance < this.detail) break;
depth += distance;
x += dx * distance;
y += dy * distance;
z += dz * distance;
}
return depth;
}
// Map depth into ASCII character
depthToAscii(depth) {
const index = Math.min(Math.floor(depth * 7 / this.detail), this.charset.length - 1);
return this.charset[index];
}
}
let asciiArt = document.getElementById('asciiArt');
let powerControl = document.getElementById('power');
let detailControl = document.getElementById('detail');
let columnsControl = document.getElementById('columns');
// Initial values
let power = parseFloat(powerControl.value);
let detail = parseFloat(detailControl.value);
let columns = parseInt(columnsControl.value);
let rows = Math.floor(columns / 2);
let mandelbulb = new Mandelbulb(power, detail, columns, rows);
let angleX = 0;
let angleY = 0;
function render() {
asciiArt.textContent = mandelbulb.render(angleX, angleY);
angleX += 0.04;
angleY += 0.03;
requestAnimationFrame(render);
}
powerControl.addEventListener('input', () => {
power = parseFloat(powerControl.value);
mandelbulb = new Mandelbulb(power, detail, columns, rows);
});
detailControl.addEventListener('input', () => {
detail = parseFloat(detailControl.value);
mandelbulb = new Mandelbulb(power, detail, columns, rows);
});
columnsControl.addEventListener('input', () => {
columns = parseInt(columnsControl.value);
rows = Math.floor(columns / 2);
mandelbulb = new Mandelbulb(power, detail, columns, rows);
});
render();
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
icons/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

9
icons/browserconfig.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="icons/mstile-150x150.png"/>
<TileColor>#33ccff</TileColor>
</tile>
</msapplication>
</browserconfig>

BIN
icons/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
icons/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
icons/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
icons/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,38 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="256.000000pt" height="256.000000pt" viewBox="0 0 256.000000 256.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,256.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1420 2448 c-25 -11 -52 -26 -60 -33 -8 -7 -29 -16 -47 -20 -18 -3
-48 -17 -67 -30 -65 -44 -72 -46 -109 -30 -19 8 -37 12 -40 8 -4 -3 -14 -2
-24 2 -39 17 -60 11 -112 -28 -29 -23 -55 -47 -58 -54 -3 -7 -21 -21 -41 -30
-20 -10 -44 -25 -53 -34 -9 -9 -23 -21 -30 -25 -8 -5 -21 -22 -29 -39 -9 -16
-26 -34 -38 -40 -12 -5 -22 -13 -22 -17 0 -5 -6 -8 -13 -8 -7 0 -24 -8 -37
-17 -24 -17 -72 -38 -115 -50 -35 -9 -124 -54 -135 -68 -6 -7 -36 -23 -68 -36
-31 -12 -59 -26 -62 -29 -3 -3 -19 -12 -37 -19 -42 -17 -60 -38 -45 -53 6 -6
8 -16 5 -23 -2 -8 0 -23 6 -34 6 -11 13 -39 16 -63 7 -58 14 -78 37 -111 11
-16 24 -24 31 -20 7 4 6 -1 -2 -11 -22 -26 -23 -74 -4 -131 20 -60 23 -199 4
-233 -19 -35 -17 -101 4 -116 17 -12 17 -13 -2 -18 -23 -6 -53 -64 -57 -111
-2 -17 -5 -39 -8 -47 -3 -8 -10 -32 -16 -53 l-11 -39 62 -30 c34 -16 76 -38
92 -48 17 -10 40 -19 52 -19 24 -1 53 -35 53 -63 0 -9 10 -19 23 -23 25 -7 34
-11 127 -53 36 -16 106 -47 155 -68 50 -21 155 -71 233 -112 107 -55 156 -75
190 -77 26 -2 72 -16 102 -31 51 -25 166 -74 299 -126 l54 -22 16 21 c9 12 27
35 40 53 13 17 32 45 41 63 9 18 27 37 38 43 12 6 32 24 44 40 12 16 25 31 29
34 3 3 10 16 14 30 13 47 47 110 58 110 7 0 25 7 41 15 16 8 34 14 40 14 14
-3 34 5 121 46 39 19 95 45 126 59 l56 24 -8 49 c-4 26 -8 69 -8 95 -1 47 -17
97 -36 110 -7 5 -5 15 4 30 7 12 13 42 13 67 -1 27 5 56 15 71 9 14 16 39 15
56 -2 26 4 35 33 55 37 26 95 98 95 121 0 7 12 24 26 37 14 13 32 37 40 53 15
28 14 31 -14 57 -16 16 -31 35 -35 44 -3 9 -24 27 -46 41 -25 16 -43 37 -51
60 -6 19 -22 42 -36 50 -13 9 -24 22 -24 29 0 7 -13 17 -28 23 -26 10 -29 16
-30 59 -1 26 -9 59 -17 74 -7 14 -17 56 -20 94 -7 67 -7 67 -48 87 -22 11 -50
20 -62 20 -12 0 -30 9 -40 20 -10 11 -22 20 -26 20 -4 0 -20 5 -36 11 -15 7
-42 11 -60 10 -71 -5 -192 84 -193 141 0 14 -58 64 -108 93 -17 11 -34 26 -37
35 -4 14 -89 91 -97 89 -2 0 -23 -10 -48 -21z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

19
icons/site.webmanifest Normal file
View File

@ -0,0 +1,19 @@
{
"name": "elia.network",
"short_name": "elia.network",
"icons": [
{
"src": "android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "android-chrome-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
],
"theme_color": "#33ccff",
"background_color": "#33ccff",
"display": "standalone"
}

View File

@ -1,15 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- SEO Tags -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="elia.network">
<meta name="description" content="swiss hosted services">
<meta name="keywords" content="ich, hasse, mein, leben">
<meta name="author" content="elia">
<!-- Social Tags for Embeds -->
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="manifest" href="icons/site.webmanifest">
<link rel="mask-icon" href="icons/safari-pinned-tab.svg" color="#33ccffaf">
<link rel="shortcut icon" href="icons/favicon.ico">
<meta name="msapplication-TileColor" content="#33ccff">
<meta name="msapplication-config" content="icons/browserconfig.xml">
<meta name="theme-color" content="#33ccff">
<meta property="og:title" content="elia.network">
<meta property="og:description" content="swiss hosted services">
<meta property="og:url" content="https://elia.network">