Successfully converting minified JavaScript code back into a readable format using ChatGPT



Frank Fiegel, an engineer at Glama.ai, which provides chatbot AI for teams, has reported that he was able to use ChatGPT to restore JavaScript code that had been 'Minified' by shortening variable names and removing line breaks and spaces to reduce space.

Using ChatGPT to reverse engineer minified JavaScript - ChatGPT for teams | Glama
https://glama.ai/blog/2024-08-29-reverse-engineering-minified-code-using-openai



The first thing that caught Fiegel's attention was the animation displayed in the background of the following site. This animation is an ASCII art made up of letters that undulates and changes rapidly, and Fiegel was curious about how this animation was implemented on the site.

Reactive Network
https://reactive.network/hackathon



After some investigation, it was discovered that the following code constitutes the animation:
[code]const { floor: ra, abs: KE, min: QE } = Math,
O5 = ['reactive.network REACTIVE.NETWORK', '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–'â–'â–“â–ˆ'],
G7 = Date.now() % 3 ? O5[1] : O5[0],
V5 = G7.length,
JE = { fps: 60 };
function eT(e, t, n, r) {
const i = t.time * 8e-5,
s = QE(t.cols, t.rows),
o = t.metrics.aspect * 0.2,
l = { x: ((4 * (ex - t.cols / 6.25)) / s) * o, y: (5 * (ey - t.rows / 4)) / s },
u = ra(KE(YE(l) - i) * V5 + (ra(ex / 1) % 2) * 2) % V5;
return G7[u];
}
const tT = () => {
const e = j.useRef(null),
[t, n] = j.useState({ height: null, width: null });
return (
j.useEffect(() => {
function r() {
n({ height: window.innerHeight, width: window.innerWidth });
}
if (typeof window < 'u') return n({ height: window.innerHeight, width: window.innerWidth }), window.addEventListener('resize', r), () => window.removeEventListener('resize', r);
}, []),
j.useEffect(() => {
const r = e.current;
if (!r) return;
const i = 12,
s = ra(t.width / i) * 1.6,
o = ra(t.height / i),
l = { aspect: s / o },
u = setInterval(() => {
let c = '';
for (let d = 0; d < o; d++) {
for (let f = 0; f < s; f++) c += eT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() });
c += `
`;
}
r.textContent = c;
}, 1e3 / JE.fps);
return () => clearInterval(u);
}, [t]),
a.jsx('div', { style: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }, children: a.jsx('div', { ref: e, style: { width: '100%', height: '100%', whiteSpace: 'pre', overflow: 'hidden' } }) })
);
};
function nT(e) {
return Math.cos(ex * ex - ey * ey);
}
const { floor: ia, abs: rT, min: iT } = Math,
D5 = ['reactive.network REACTIVE.NETWORK', '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–'â–'â–“â–ˆ'],
X7 = Date.now() % 3 ? D5[1] : D5[0],
F5 = X7.length,
sT = { fps: 60 };
function oT(e, t, n, r) {
const i = t.time * 8e-5,
s = iT(t.cols, t.rows),
o = t.metrics.aspect * 0.2,
l = { x: ((4 * (ex - t.cols / 6.25)) / s) * o, y: (5 * (ey - t.rows / 4)) / s },
u = ia(rT(nT(l) - i) * F5 + (ia(ex / 1) % 2) * 2) % F5;
return X7[u];
}
const aT = () => {
const e = j.useRef(null),
[t, n] = j.useState({ height: null, width: null });
return (
j.useEffect(() => {
function r() {
n({ height: window.innerHeight, width: window.innerWidth });
}
if (typeof window < 'u') return n({ height: window.innerHeight, width: window.innerWidth }), window.addEventListener('resize', r), () => window.removeEventListener('resize', r);
}, []),
j.useEffect(() => {
const r = e.current;
if (!r) return;
const i = 12,
s = ia(t.width / i) * 1.6,
o = ia(t.height / i),
l = { aspect: s / o },
u = setInterval(() => {
let c = '';
for (let d = 0; d < o; d++) {
for (let f = 0; f < s; f++) c += oT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() });
c += `
`;
}
r.textContent = c; }, 1e3 / sT.fps);
return () => clearInterval(u);
}, [t]),
a.jsx('div', { style: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }, children: a.jsx('div', { ref: e, style: { width: '100%', height: '100%', whiteSpace: 'pre', overflow: 'hidden' } }) })
);
},[/code]



However, this code was minified and difficult to read, so Fiegel asked ChatGPT to explain the code.

ChatGPT responded, 'The code provided is quite complex, but we will break it down into a more understandable form and explain each part and its function. This code is from a React application, which uses JavaScript to define several functions and components to generate dynamic text content (probably ASCII art or some visual effect using a predefined set of characters). It also contains utility functions for event handling, performance timing, and HTML content conversion.' The main components of the code are broken down into the following three parts:

1: Selecting the character set
[code]const { floor: ra, abs: KE, min: QE } = Math;
const O5 = ['reactive.network REACTIVE.NETWORK', '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–'â–'â–“â–ˆ'];
const G7 = Date.now() % 3 ? O5[1] : O5[0];
const V5 = G7.length;[/code]



This section defines the character set that will be used by the code that follows, allowing for more variation and different looks in the visual effects that are generated, and adding an element of dynamics by selecting the character set based on time, allowing for different results on each run.

2: About the function that dynamically selects characters (eT function)
[code]function eT(e, t, n, r) {
const i = t.time * 8e-5;
const s = QE(t.cols, t.rows);
const o = t.metrics.aspect * 0.2;
const l = { x: ((4 * (ex - t.cols / 6.25)) / s) * o, y: (5 * (ey - t.rows / 4)) / s };
const u = ra(KE(YE(l) - i) * V5 + (ra(ex / 1) % 2) * 2) % V5;
return G7[u];
}[/code]



This function selects a specific character from a character set based on the input location (x,y coordinates) and the current time. By selecting different characters depending on the location and time, it creates dynamic, changing patterns.

3: Structure and function of React component (tT) for displaying dynamic content
[code]const tT = () => {
const e = j.useRef(null);
const [t, n] = j.useState({ height: null, width: null });
j.useEffect(() => {
function r() {
n({ height: window.innerHeight, width: window.innerWidth });
}
if (typeof window < 'u') {
n({ height: window.innerHeight, width: window.innerWidth });
window.addEventListener('resize', r);
return () => window.removeEventListener('resize', r);
}
}, []);

j.useEffect(() => {
const r = e.current;
if (!r) return;
const i = 12;
const s = ra(t.width / i) * 1.6;
const o = ra(t.height / i);
const l = { aspect: s / o };
const u = setInterval(() => {
let c = '';
for (let d = 0; d < o; d++) {
for (let f = 0; f < s; f++) c += eT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() });
c += `
`;
}
r.textContent = c;
}, 1e3 / JE.fps);
return () => clearInterval(u);
}, [t]);

return a.jsx('div', { style: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }, children: a.jsx('div', { ref: e, style: { width: '100%', height: '100%', whiteSpace: 'pre', overflow: 'hidden' } }) });
};[/code]



This component is responsible for generating and displaying dynamic text-based visual effects that update rapidly as the window resizes, and when combined with eT functions it creates complex patterns that change over time.

When Fiegel asked ChatGPT to 'implement the equivalent code in TypeScript so that it's human-readable,' the company output the following code:
[code]import React, { useEffect, useRef, useState } from 'react';

const selectedCharacterSet =
'$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–'â–'â–“â–ˆ';
const characterSetLength = selectedCharacterSet.length;

const calculateCharacter = (
x: number,
y: number,
cols: number,
rows: number,
aspect: number,
time: number,
): string => {
const timeFactor = time * 0.000_08;
const size = Math.min(cols, rows);
const aspectRatio = aspect * 0.2;
const position = {
x: ((4 * (x - cols / 6.25)) / size) * aspectRatio,
y: (5 * (y - rows / 4)) / size,
};

const index =
Math.floor(
Math.abs(
Math.cos(position.x * position.x - position.y * position.y) -
timeFactor,
) *
characterSetLength +
(Math.floor(x) % 2) * 2,
) % characterSetLength;
return selectedCharacterSet[index];
};

type Size = {
height: null | number;
width: null | number;
};

const AsciiArtGenerator: React.FC = () => {
const textRef = useRef (null);
const [size, setSize] = useState ({ height: null, width: null });

useEffect(() => {
const handleResize = () => {
setSize({ height: window.innerHeight, width: window.innerWidth });
};

// Initial size setting
handleResize();

window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);

useEffect(() => {
const element = textRef.current;
if (!element || !size.width || !size.height) return undefined;

const cols = Math.floor(size.width / 12) * 1.6;
const rows = Math.floor(size.height / 12);
const aspectRatio = cols / rows;

const intervalId = setInterval(() => {
let content = '';

for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
content += calculateCharacter(
x,
y,
Cols,
rows,
aspectRatio,
Date.now(),
);
}

content += '\n';
}

element.textContent = content;
}, 1_000 / 60); // 60 fps

return () => {
clearInterval(intervalId);
};
}, [size]);

return (

ref={textRef}
style={{
height: '100%',
overflow: 'hidden',
whiteSpace: 'pre',
width: '100%',
}}
/>
);
};

export default () => {
return ;
};[/code]



When Fiegel actually tried this code, he found that the output was almost identical to the original animation while still ensuring readability.



Although there were some actual differences, it was later pointed out on the social news site Hacker News that there was a mistake in the copy and paste of the code due to the character code, and by correcting it, the animation was output that was identical to the original.

in Software, Posted by log1i_yk