Skip to content

Instantly share code, notes, and snippets.

@Krazete
Last active July 5, 2024 04:51
Show Gist options
  • Save Krazete/e2c0f77702ae95884e0556696e9789e3 to your computer and use it in GitHub Desktop.
Save Krazete/e2c0f77702ae95884e0556696e9789e3 to your computer and use it in GitHub Desktop.
Injects cognition.kizunaai.com with a playable DOOM. See comments for keyboard controls.
/* copied from https://github.com/healeycodes/doom-checkboxes/blob/main/doom.js */
var memory = new WebAssembly.Memory({ initial: 108 });
function readWasmString(offset, length) {
const bytes = new Uint8Array(memory.buffer, offset, length);
return new TextDecoder("utf8").decode(bytes);
}
function consoleLogString(offset, length) {
const string = readWasmString(offset, length);
console.log('"' + string + '"');
}
function appendOutput(style) {
return function (offset, length) {
const lines = readWasmString(offset, length).split("\n");
for (var i = 0; i < lines.length; ++i) {
if (lines[i].length == 0) {
continue;
}
}
};
}
/*stats about how often doom polls the time*/
var getms_calls_total = 0;
var getms_calls = 0; /* in current second */
window.setInterval(function () {
getms_calls_total += getms_calls;
getms_calls = 0;
}, 1000);
function getMilliseconds() {
++getms_calls;
return performance.now();
}
/*doom is rendered here*/
const DoomCanvas = document.createElement("canvas");
DoomCanvas.width = 640;
DoomCanvas.height = 400;
var vw = 640;
var vh = 400;
const doom_screen_width = 320 * 2;
const doom_screen_height = 200 * 2;
/*printing stats*/
var number_of_draws_total = 0;
var number_of_draws = 0; /* in current second */
window.setInterval(function () {
number_of_draws_total += number_of_draws;
number_of_draws = 0;
}, 1000);
function drawDoomCanvas(ptr) {
var doom_screen = new Uint8ClampedArray(
memory.buffer,
ptr,
doom_screen_width * doom_screen_height * 4
);
var render_screen = new ImageData(
doom_screen,
doom_screen_width,
doom_screen_height
);
var ctx = DoomCanvas.getContext("2d");
ctx.putImageData(render_screen, 0, 0);
++number_of_draws;
}
/*These functions will be available in WebAssembly. We also share the memory to share larger amounts of data with javascript, e.g. strings of the video output.*/
var importObject = {
js: {
js_console_log: appendOutput("log"),
js_stdout: appendOutput("stdout"),
js_stderr: appendOutput("stderr"),
js_milliseconds_since_start: getMilliseconds,
js_draw_screen: drawDoomCanvas,
},
env: {
memory: memory,
},
};
const response = await fetch("https://raw.githubusercontent.com/healeycodes/doom-checkboxes/main/doom.wasm");
const buffer = await response.arrayBuffer();
const obj = await WebAssembly.instantiate(buffer, importObject).then(
(obj) => {
/*Initialize Doom*/
obj.instance.exports.main();
/*input handling*/
let doomKeyCode = function (keyCode) {
/* Doom seems to use mostly the same keycodes, except for the following (maybe I'm missing a few.) */
switch (keyCode) {
case 8:
return 127; /* KEY_BACKSPACE */
case 17:
return 0x80 + 0x1d; /* KEY_RCTRL */
case 18:
return 0x80 + 0x38; /* KEY_RALT */
case 37:
return 0xac; /* KEY_LEFTARROW */
case 38:
return 0xad; /* KEY_UPARROW */
case 39:
return 0xae; /* KEY_RIGHTARROW */
case 40:
return 0xaf; /* KEY_DOWNARROW */
default:
if (keyCode >= 65 /*A*/ && keyCode <= 90 /*Z*/) {
return keyCode + 32; /* ASCII to lower case */
}
if (keyCode >= 112 /*F1*/ && keyCode <= 123 /*F12*/) {
return keyCode + 75; /* KEY_F1 */
}
return keyCode;
}
};
let keyDown = function (keyCode) {
obj.instance.exports.add_browser_event(0 /*KeyDown*/, keyCode);
};
let keyUp = function (keyCode) {
obj.instance.exports.add_browser_event(1 /*KeyUp*/, keyCode);
};
/*keyboard input*/
window.addEventListener(
"keydown",
function (event) {
keyDown(doomKeyCode(event.keyCode));
event.preventDefault();
},
false
);
window.addEventListener(
"keyup",
function (event) {
keyUp(doomKeyCode(event.keyCode));
event.preventDefault();
},
false
);
/*printing stats*/
var number_of_animation_frames = 0; /* in current second */
window.setInterval(function () {
number_of_animation_frames = 0;
}, 1000);
/*Main game loop*/
function step(timestamp) {
++number_of_animation_frames;
obj.instance.exports.doom_loop_step();
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);
window.doomLoaded = true;
}
);
document.body.appendChild(DoomCanvas);
DoomCanvas.style.position = "absolute";
DoomCanvas.style.bottom = "44px";
DoomCanvas.style.zIndex = "2";
DoomCanvas.style.width = "250px";
/* copied from https://gist.github.com/Krazete/0fcfe6eeea57961df821f30c512cf2b2 */
var video = DoomCanvas;
var dpr = Math.ceil(window.devicePixelRatio || 1); /* for high dpi screens */
var canvas = document.getElementsByTagName("canvas")[0];
var context = canvas.getContext("2d", {willReadFrequently: true});
var cw, ch, ix, iy, r;
var vcanvas = document.createElement("canvas");
var vcontext = vcanvas.getContext("2d");
var vcw, vch;
function getRectInfo() {
var cd = context.getImageData(0, 0, cw, ch);
var x1count = 0;
var x0count = 0;
for (var x = 0; x < cd.width; x++) {
var i = 4 * x;
if (cd.data[i] || cd.data[i + 1] || cd.data[i + 2]) {
if (x0count) {
break;
}
x1count++;
}
else {
x0count++;
}
}
var y1count = 0;
var y0count = 0;
for (var y = 0; y < cd.height; y++) {
var i = 4 * cd.width * y;
if (cd.data[i] || cd.data[i + 1] || cd.data[i + 2]) {
if (y0count) {
break;
}
y1count++;
}
else {
y0count++;
}
}
return {
w: x1count / dpr,
h: y1count / dpr,
dx: (x1count + x0count) / dpr,
dy: (y1count + y0count) / dpr
};
}
function recalculate() {
cw = canvas.width / dpr;
ch = canvas.height / dpr;
/* scale video resolution to fit canvas */
var iw, ih;
if (vw / vh < cw / ch) {
iw = vw * ch / vh;
ih = ch;
}
else {
iw = cw;
ih = vh * cw / vw;
}
ix = (cw - iw) / 2;
iy = (ch - ih) / 2;
/* get rect info */
r = getRectInfo();
/* prepare video pixelation and adjust iw, ih, ix, iy with rect info */
vcw = Math.ceil(iw / r.dx);
vch = Math.ceil(ih / r.dy);
vcanvas.width = vcw;
vcanvas.height = vch;
ix = Math.floor(ix / r.dx) * r.dx;
iy = Math.floor(iy / r.dy) * r.dy;
}
/* colors
black #000000
white #ffffff
gray #0a0a0a
pink #eb50ae
blue #0000ff
*/
function play() {
vcontext.drawImage(video, 0, 0, vcw, vch);
var vcd = vcontext.getImageData(0, 0, vcw, vch);
context.beginPath();
for (var y = 0; y < vcd.height; y++) {
for (var x = 0; x < vcd.width; x++) {
var i = 4 * (vcd.width * y + x);
var j = (vcd.data[i] + vcd.data[i + 1] + vcd.data[i + 2]) / 3;
if (j >= 96) {context.fillStyle = "#ffffff";}
else if (vcd.data[i] >= 80) {context.fillStyle = "#eb50ae";}
else if (vcd.data[i + 2] >= 64) {context.fillStyle = "#0000ff";}
else {context.fillStyle = "#0a0a0a";} context.fillRect(ix + x * r.dx, iy + y * r.dy, r.w, r.h);
}
}
if (!video.paused) {
requestAnimationFrame(play);
}
};
recalculate();
play();
window.addEventListener("resize", recalculate);
@Krazete
Copy link
Author

Krazete commented Jul 5, 2024

Preview: https://youtu.be/C15ZY6k9Vys

Code Credits

(they were very sloppily edited together in order to work on https://cognition.kizunaai.com)

Controls

  • Enter: Enter
  • Move: / / /
  • Shoot: Ctrl
  • Open Gates: Space
  • Strafe: Alt + / / /

(it's the same as https://healeycodes.github.io/doom-checkboxes)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment