Muchos lo han hecho antes y por qué no intentarlo: ver en profundidad cómo es programar con IA. El primer ejemplo que se me ocurrió es muy fácil para la IA porque realmente se lo han pedido tantas veces que ya tiene el código guardado en el tintero. Pero veamos qué sucede al darle una simple instrucción a ChatGPT y Gemini y analizaremos el código que generan.
El prompt utilizado
El prompt es muy simple:
“Crea un simulador de lluvia 2D usando HTML, Javascript y CSS.”
Algo a tener en cuenta: usaré las versiones de pago de ambos y el modelo thinking. La lógica en programación suele ser la misma a la hora de dar un resultado, pero hay diferencias en cómo trabajan las IA que te pueden sorprender.
Resultado y comparación
En la siguiente captura vemos una imagen rápida y comparativa de estas dos IA visualmente y debajo dejaré el código que generaron cada una para probarlo.
Nota para principiantes
Para quienes nunca programaron y quieren probarlo en vivo: solo deben copiar el código en un archivo nuevo .txt, pegarlo y cambiar la extensión a .html. El archivo debería quedar así:
“Simulador de lluvia.html”
y no “Simulador de lluvia.html.txt”.
Gemini a en el lado Izquierdo y ChatGPT a la derecha.

Código generado por cada IA
Código desarrollado por Gemini
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simulador de Lluvia 2D</title>
<style>
/* Estilos básicos para pantalla completa */
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #050505; /* Fondo oscuro nocturno */
}
canvas {
display: block;
}
/* Superposición de texto opcional */
#ui {
position: absolute;
top: 20px;
left: 20px;
color: rgba(255, 255, 255, 0.3);
font-family: sans-serif;
pointer-events: none;
user-select: none;
}
</style>
</head>
<body>
<div id="ui">Simulador de Lluvia 2D</div>
<canvas id="lluviaCanvas"></canvas>
<script>
const canvas = document.getElementById('lluviaCanvas');
const ctx = canvas.getContext('2d');
let width, height;
let gotas = [];
// Configuración de la lluvia
const numeroGotas = 500;
const velocidadMin = 10;
const velocidadMax = 20;
// Ajustar el canvas al tamaño de la ventana
function ajustarTamano() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
}
window.addEventListener('resize', ajustarTamano);
ajustarTamano();
// Clase Gota
class Gota {
constructor() {
this.init();
}
init() {
this.x = Math.random() * width;
this.y = Math.random() * -height; // Empiezan fuera de la pantalla arriba
this.velocidad = Math.random() * (velocidadMax - velocidadMin) + velocidadMin;
this.longitud = Math.random() * 20 + 10;
this.opacidad = Math.random() * 0.5 + 0.2;
this.grosor = Math.random() * 1.5 + 0.5;
}
dibujar() {
ctx.beginPath();
ctx.strokeStyle = `rgba(174, 194, 224, ${this.opacidad})`;
ctx.lineWidth = this.grosor;
ctx.lineCap = 'round';
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x, this.y + this.longitud);
ctx.stroke();
}
actualizar() {
this.y += this.velocidad;
// Si la gota sale por abajo, reinicia arriba
if (this.y > height) {
this.init();
this.y = -20;
}
}
}
// Crear el array de gotas
function crearLluvia() {
for (let i = 0; i < numeroGotas; i++) {
gotas.push(new Gota());
}
}
// Bucle de animación
function animar() {
// Fondo con un poco de transparencia para crear efecto de estela (trail)
ctx.fillStyle = 'rgba(5, 5, 5, 0.5)';
ctx.fillRect(0, 0, width, height);
gotas.forEach(gota => {
gota.actualizar();
gota.dibujar();
});
requestAnimationFrame(animar);
}
crearLluvia();
animar();
</script>
</body>
</html>
Código desarrollado por ChatGPT
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Lluvia 2D (Canvas)</title>
<style>
:root{
--bg1:#05070d;
--bg2:#0b1630;
--panel: rgba(0,0,0,.55);
--text: rgba(255,255,255,.85);
--muted: rgba(255,255,255,.65);
--border: rgba(255,255,255,.12);
}
html, body { height: 100%; margin: 0; }
body{
background: radial-gradient(1200px 800px at 70% 20%, var(--bg2), var(--bg1));
overflow: hidden;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif;
color: var(--text);
}
canvas{
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
display: block;
}
.hud{
position: fixed;
left: 16px;
top: 16px;
padding: 12px 12px 10px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 12px;
backdrop-filter: blur(8px);
box-shadow: 0 10px 30px rgba(0,0,0,.35);
width: min(360px, calc(100vw - 32px));
user-select: none;
}
.hud h1{
font-size: 14px;
margin: 0 0 8px;
letter-spacing: .3px;
font-weight: 650;
color: var(--text);
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
}
.hud small{ color: var(--muted); font-weight: 500; }
.row{
display: grid;
grid-template-columns: 1fr auto;
gap: 10px;
align-items: center;
margin: 8px 0;
}
label{
font-size: 12px;
color: var(--muted);
}
input[type="range"]{ width: 100%; }
.btns{
display: flex;
gap: 8px;
margin-top: 10px;
flex-wrap: wrap;
}
button{
background: rgba(255,255,255,.08);
border: 1px solid var(--border);
color: var(--text);
padding: 8px 10px;
border-radius: 10px;
cursor: pointer;
font-weight: 600;
font-size: 12px;
}
button:hover{ background: rgba(255,255,255,.12); }
.kbd{
margin-top: 10px;
font-size: 11px;
color: var(--muted);
line-height: 1.35;
}
.pill{
border: 1px solid var(--border);
background: rgba(0,0,0,.25);
padding: 2px 8px;
border-radius: 999px;
font-size: 11px;
color: var(--muted);
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div class="hud">
<h1>
<span>Lluvia 2D <span class="pill">Canvas</span></span>
<small id="fps">— fps</small>
</h1>
<div class="row">
<label for="density">Densidad (gotas)</label>
<span id="densityVal" class="pill"></span>
</div>
<input id="density" type="range" min="100" max="3000" step="50" value="1200"/>
<div class="row">
<label for="wind">Viento</label>
<span id="windVal" class="pill"></span>
</div>
<input id="wind" type="range" min="-10" max="10" step="0.1" value="1.2"/>
<div class="row">
<label for="gravity">Gravedad</label>
<span id="gravityVal" class="pill"></span>
</div>
<input id="gravity" type="range" min="0.2" max="2.2" step="0.01" value="1.1"/>
<div class="row">
<label for="splash">Splash</label>
<span id="splashVal" class="pill"></span>
</div>
<input id="splash" type="range" min="0" max="1" step="0.01" value="0.55"/>
<div class="btns">
<button id="toggle">Pausar</button>
<button id="clear">Limpiar</button>
<button id="storm">Tormenta</button>
<button id="calm">Llovizna</button>
</div>
<div class="kbd">
Atajos: <b>Espacio</b> pausar · <b>C</b> limpiar · <b>↑/↓</b> densidad · <b>←/→</b> viento
</div>
</div>
<script>
"use strict";
// ===== Canvas setup =====
const canvas = document.getElementById("c");
const ctx = canvas.getContext("2d", { alpha: true });
const DPR = Math.min(2, window.devicePixelRatio || 1);
let W = 0, H = 0;
function resize(){
W = Math.floor(window.innerWidth);
H = Math.floor(window.innerHeight);
canvas.width = Math.floor(W * DPR);
canvas.height = Math.floor(H * DPR);
ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
}
window.addEventListener("resize", resize, { passive: true });
resize();
// ===== UI =====
const elDensity = document.getElementById("density");
const elWind = document.getElementById("wind");
const elGravity = document.getElementById("gravity");
const elSplash = document.getElementById("splash");
const fpsEl = document.getElementById("fps");
const densityVal = document.getElementById("densityVal");
const windVal = document.getElementById("windVal");
const gravityVal = document.getElementById("gravityVal");
const splashVal = document.getElementById("splashVal");
function syncLabels(){
densityVal.textContent = `${Number(elDensity.value).toFixed(0)}`;
windVal.textContent = `${Number(elWind.value).toFixed(1)}`;
gravityVal.textContent = `${Number(elGravity.value).toFixed(2)}`;
splashVal.textContent = `${Math.round(Number(elSplash.value) * 100)}%`;
}
[elDensity, elWind, elGravity, elSplash].forEach(el => {
el.addEventListener("input", syncLabels);
});
syncLabels();
// ===== Simulation =====
// Raindrop model: each drop is a slanted segment.
const drops = [];
const splashes = [];
function rand(min, max){ return Math.random() * (max - min) + min; }
function makeDrop(spawnTop = true){
const len = rand(8, 22);
const z = Math.random(); // depth: affects speed + opacity
const thickness = z < 0.2 ? 1.7 : (z < 0.6 ? 1.25 : 1.0);
return {
x: rand(-50, W + 50),
y: spawnTop ? rand(-H, 0) : rand(0, H),
vy: rand(280, 900) * (0.55 + z), // px/s
len,
z,
w: thickness,
// a bit of horizontal drift baked in, plus global wind
vx: rand(-30, 30) * (0.25 + z),
};
}
function setDropCount(target){
const t = Math.max(0, Math.floor(target));
while(drops.length < t) drops.push(makeDrop(true));
while(drops.length > t) drops.pop();
}
function addSplash(x, y, intensity){
// intensity in [0..1]
const n = Math.floor(3 + intensity * 8);
for(let i=0;i<n;i++){
const a = rand(-Math.PI, 0); // upwards semicircle
const sp = rand(40, 220) * (0.5 + intensity);
splashes.push({
x, y,
vx: Math.cos(a) * sp,
vy: Math.sin(a) * sp,
life: rand(0.18, 0.35),
t: 0,
r: rand(0.8, 1.6),
alpha: rand(0.3, 0.75),
});
}
}
// Initial population
setDropCount(Number(elDensity.value));
// ===== Rendering =====
function drawBackgroundFade(alpha){
// Slight trail effect: paint a translucent rect.
ctx.fillStyle = `rgba(5,7,13,${alpha})`;
ctx.fillRect(0, 0, W, H);
}
function drawDrops(){
const wind = Number(elWind.value);
// Color/opacity based on depth (z). Keep it simple.
for (let i=0; i<drops.length; i++){
const d = drops[i];
const tilt = wind * 2.2 + d.vx * 0.02; // slope
const x2 = d.x + tilt;
const y2 = d.y + d.len;
const a = 0.12 + d.z * 0.22; // opacity
ctx.strokeStyle = `rgba(200,220,255,${a})`;
ctx.lineWidth = d.w;
ctx.beginPath();
ctx.moveTo(d.x, d.y);
ctx.lineTo(x2, y2);
ctx.stroke();
}
}
function drawSplashes(){
for (let i=splashes.length-1; i>=0; i--){
const p = splashes[i];
const k = 1 - (p.t / p.life);
const a = Math.max(0, p.alpha * k);
ctx.fillStyle = `rgba(210,230,255,${a})`;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r * (0.6 + (1-k)), 0, Math.PI*2);
ctx.fill();
}
}
// ===== Loop =====
let last = performance.now();
let paused = false;
// fps counter
let fpsAcc = 0, fpsN = 0, fpsLast = performance.now();
function step(now){
requestAnimationFrame(step);
const dt = Math.min(0.033, (now - last) / 1000);
last = now;
// FPS
fpsAcc += 1 / Math.max(dt, 1e-6);
fpsN++;
if (now - fpsLast > 350){
const fps = fpsAcc / fpsN;
fpsEl.textContent = `${fps.toFixed(0)} fps`;
fpsAcc = 0; fpsN = 0; fpsLast = now;
}
// Keep count in sync (slider changes)
setDropCount(Number(elDensity.value));
if (paused) return;
const wind = Number(elWind.value);
const g = Number(elGravity.value);
const splashIntensity = Number(elSplash.value);
// Fade (trail). More rain => stronger trails, looks nicer.
const trail = 0.18 + Math.min(0.25, drops.length / 6000);
drawBackgroundFade(trail);
// Update drops
for (let i=0; i<drops.length; i++){
const d = drops[i];
d.y += d.vy * dt * g;
d.x += (wind * 55 + d.vx) * dt;
// Wrap horizontally
if (d.x < -60) d.x = W + 60;
if (d.x > W + 60) d.x = -60;
// Hit ground
if (d.y > H + 40){
if (splashIntensity > 0 && Math.random() < (0.25 + d.z * 0.4)){
addSplash(
Math.max(0, Math.min(W, d.x)),
H - 2,
splashIntensity * (0.45 + d.z * 0.7)
);
}
// respawn at top
drops[i] = makeDrop(true);
drops[i].y = rand(-120, -10);
}
}
// Update splash particles
for (let i=splashes.length-1; i>=0; i--){
const p = splashes[i];
p.t += dt;
if (p.t >= p.life){
splashes.splice(i, 1);
continue;
}
// simple ballistic arc + damping
p.vy += 580 * dt; // gravity on particles
p.x += p.vx * dt;
p.y += p.vy * dt;
p.vx *= (1 - 2.2 * dt);
p.vy *= (1 - 1.6 * dt);
}
// Draw
drawDrops();
drawSplashes();
}
// Initial clear and start
ctx.fillStyle = "rgba(5,7,13,1)";
ctx.fillRect(0, 0, W, H);
requestAnimationFrame(step);
// ===== Controls =====
const btnToggle = document.getElementById("toggle");
const btnClear = document.getElementById("clear");
const btnStorm = document.getElementById("storm");
const btnCalm = document.getElementById("calm");
btnToggle.addEventListener("click", () => {
paused = !paused;
btnToggle.textContent = paused ? "Reanudar" : "Pausar";
});
function clearAll(){
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = "rgba(5,7,13,1)";
ctx.fillRect(0, 0, W, H);
splashes.length = 0;
// respawn drops in place (prevents “holes”)
for(let i=0;i<drops.length;i++) drops[i] = makeDrop(false);
}
btnClear.addEventListener("click", clearAll);
btnStorm.addEventListener("click", () => {
elDensity.value = 2400;
elWind.value = 3.8;
elGravity.value = 1.35;
elSplash.value = 0.75;
syncLabels();
clearAll();
});
btnCalm.addEventListener("click", () => {
elDensity.value = 700;
elWind.value = 0.7;
elGravity.value = 0.95;
elSplash.value = 0.35;
syncLabels();
clearAll();
});
window.addEventListener("keydown", (e) => {
if (e.code === "Space"){
e.preventDefault();
btnToggle.click();
} else if (e.key.toLowerCase() === "c"){
clearAll();
} else if (e.key === "ArrowUp"){
elDensity.value = Math.min(3000, Number(elDensity.value) + 100);
syncLabels();
} else if (e.key === "ArrowDown"){
elDensity.value = Math.max(100, Number(elDensity.value) - 100);
syncLabels();
} else if (e.key === "ArrowRight"){
elWind.value = Math.min(10, Number(elWind.value) + 0.2);
syncLabels();
} else if (e.key === "ArrowLeft"){
elWind.value = Math.max(-10, Number(elWind.value) - 0.2);
syncLabels();
}
}, { passive: false });
</script>
</body>
</html>
Análisis del código generado
Lo primero a destacar son las funcionalidades. Gemini se tomó la instrucción de forma literal y creó una web que muestra lluvia sobre un fondo negro. ChatGPT, en cambio, desarrolló una interfaz gráfica con valores configurables.
ChatGPT escribió aproximadamente 426 líneas de código, mientras que Gemini unas 121. No es que más líneas signifique mejor código, pero la diferencia muestra cómo ChatGPT rellena la falta de contexto interpretando la intención del usuario.
ChatGPT tiende a interpretar la idea general y completar el desarrollo evitando repreguntas, algo clave para crear flujos de trabajo más ágiles.
Diferencias en la lógica de razonamiento
ChatGPT sugirió mejoras directamente:
“Si quieres que se vea más realista (blur, relámpagos, charcos, parallax de fondo, lluvia contra cristal), te lo ajusto en 2 minutos”.
Gemini respondió:
“¿Te gustaría que añada algún efecto adicional…?”
No es que Gemini no pueda hacerlo, sino que suele requerir que se lo pidas explícitamente.
Esto revela mucho sobre cómo razonan los LLM: uno actúa de forma proactiva, el otro espera instrucciones claras. Esto impacta directamente en el flujo de trabajo.
Comparativa conceptual: proactividad vs literalidad
Gemini tiende a una lógica plana y conservadora: no toma riesgos ni añade contexto extra. ChatGPT es más proactivo y arriesgado, como ese empleado que se adelanta al pedido, entiende el contexto y actúa sin esperar órdenes.
Copilot Vs Codex en Visual Studio Code
La versatilidad de usar LLM dentro del editor es enorme. Entienden el contexto del repositorio y ayudan a resolver problemas rápidos.
Puntos en contra
- A veces inventan funciones que no existen.
- No siempre entienden tu lógica interna.
Puntos a favor
- Facilitan entender código ajeno.
- Ahorran tiempo leyendo documentación.
Resultado del simulador con Copilot y Codex
Resultado Copilot
[Imagen]

Código generado por Copilot:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simulador de Lluvia 2D</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 100vw;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: Arial, sans-serif;
overflow: hidden;
}
canvas {
border: 2px solid white;
background: rgba(0, 0, 0, 0.1);
display: block;
}
.controls {
position: absolute;
top: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.9);
padding: 15px;
border-radius: 8px;
color: #333;
font-size: 14px;
}
.controls label {
display: block;
margin: 8px 0;
}
input[type="range"] {
width: 150px;
}
</style>
</head>
<body>
<canvas id="rainCanvas"></canvas>
<div class="controls">
<label>Intensidad: <input type="range" id="intensity" min="10" max="200" value="100"></label>
<label>Viento: <input type="range" id="wind" min="-5" max="5" value="0" step="0.1"></label>
<label>Gravedad: <input type="range" id="gravity" min="1" max="10" value="5" step="0.5"></label>
</div>
<script>
const canvas = document.getElementById('rainCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let raindrops = [];
let settings = {
intensity: 100,
wind: 0,
gravity: 5
};
class Raindrop {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height - canvas.height;
this.length = Math.random() * 15 + 10;
this.width = Math.random() * 2 + 1;
this.speedY = Math.random() * 5 + settings.gravity;
this.speedX = settings.wind;
this.opacity = Math.random() * 0.5 + 0.5;
}
update() {
this.y += this.speedY;
this.x += this.speedX;
this.speedY += settings.gravity * 0.1;
if (this.y > canvas.height) {
this.y = -this.length;
this.x = Math.random() * canvas.width;
}
if (this.x > canvas.width) {
this.x = -10;
} else if (this.x < -10) {
this.x = canvas.width;
}
}
draw() {
ctx.strokeStyle = `rgba(200, 220, 255, ${this.opacity})`;
ctx.lineWidth = this.width;
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.speedX * 2, this.y + this.length);
ctx.stroke();
}
}
function initRain() {
raindrops = [];
for (let i = 0; i < settings.intensity; i++) {
raindrops.push(new Raindrop());
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
raindrops.forEach(drop => {
drop.update();
drop.draw();
});
requestAnimationFrame(animate);
}
document.getElementById('intensity').addEventListener('input', (e) => {
settings.intensity = parseInt(e.target.value);
initRain();
});
document.getElementById('wind').addEventListener('input', (e) => {
settings.wind = parseFloat(e.target.value);
});
document.getElementById('gravity').addEventListener('input', (e) => {
settings.gravity = parseFloat(e.target.value);
});
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
initRain();
animate();
</script>
</body>
</html>
Copilot no tuvo un buen día: el programa no funcionaba ni se veía correctamente.
Resultado Codex

Código generado por Codex de OpenAI:
!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Simulador de Lluvia 2D</title>
<style>
:root {
--bg-top: #0b1b33;
--bg-bottom: #0f304d;
--drop: #7fc8ff;
--accent: #ffcc66;
--panel: rgba(255, 255, 255, 0.07);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body, html { width: 100%; height: 100%; overflow: hidden; font-family: "Segoe UI", system-ui, sans-serif; color: #e7f3ff; background: linear-gradient(180deg, var(--bg-top), var(--bg-bottom)); }
canvas { display: block; width: 100vw; height: 100vh; }
.ui {
position: fixed; top: 12px; left: 12px;
padding: 12px 14px; border-radius: 12px; background: var(--panel); backdrop-filter: blur(6px);
display: flex; gap: 10px; align-items: center; box-shadow: 0 8px 30px rgba(0,0,0,0.25);
}
.ui label { font-size: 13px; font-weight: 600; color: #dbe9ff; }
.ui input[type="range"] { accent-color: var(--accent); }
.pill {
display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 999px;
background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.15); cursor: pointer; user-select: none;
transition: transform 0.1s ease, border-color 0.2s ease;
}
.pill:hover { transform: translateY(-1px); border-color: rgba(255,255,255,0.35); }
.pill input { margin: 0; }
</style>
</head>
<body>
<canvas id="rain"></canvas>
<div class="ui">
<label>Intensidad
<input id="intensity" type="range" min="50" max="1000" value="400" step="10" />
</label>
<label>Viento
<input id="wind" type="range" min="-2" max="2" value="0" step="0.1" />
</label>
<label class="pill">
<input id="splash" type="checkbox" checked />
Salpique
</label>
</div>
<script>
(() => {
const canvas = document.getElementById("rain");
const ctx = canvas.getContext("2d");
let w, h, drops = [];
const ui = {
intensity: document.getElementById("intensity"),
wind: document.getElementById("wind"),
splash: document.getElementById("splash"),
};
const resize = () => {
w = canvas.width = innerWidth * devicePixelRatio;
h = canvas.height = innerHeight * devicePixelRatio;
canvas.style.width = "100vw";
canvas.style.height = "100vh";
};
addEventListener("resize", resize);
resize();
const rand = (min, max) => Math.random() * (max - min) + min;
const makeDrop = () => ({
x: rand(0, w),
y: rand(-h, 0),
len: rand(10, 25) * devicePixelRatio,
speed: rand(0.15, 0.3) * devicePixelRatio,
thickness: rand(1, 2) * devicePixelRatio,
sway: rand(-0.3, 0.3) * devicePixelRatio,
});
const ensureDrops = () => {
const target = Number(ui.intensity.value);
while (drops.length < target) drops.push(makeDrop());
while (drops.length > target) drops.pop();
};
let splashPool = [];
const addSplash = (x, y) => {
if (!ui.splash.checked) return;
splashPool.push({ x, y, life: 1, radius: 0 });
if (splashPool.length > 100) splashPool.shift();
};
const update = () => {
ensureDrops();
const wind = Number(ui.wind.value) * devicePixelRatio;
ctx.clearRect(0, 0, w, h);
ctx.lineCap = "round";
ctx.strokeStyle = "rgba(127, 200, 255, 0.75)";
drops.forEach(d => {
ctx.lineWidth = d.thickness;
ctx.beginPath();
ctx.moveTo(d.x, d.y);
ctx.lineTo(d.x + wind * d.len * 0.1 + d.sway, d.y + d.len);
ctx.stroke();
d.x += wind + d.sway;
d.y += d.len * d.speed;
if (d.y > h) {
addSplash(d.x, h - 2 * devicePixelRatio);
Object.assign(d, makeDrop());
d.y = rand(-h * 0.2, 0);
}
});
splashPool.forEach(s => {
s.radius += 0.8 * devicePixelRatio;
s.life -= 0.02;
});
splashPool = splashPool.filter(s => s.life > 0);
splashPool.forEach(s => {
ctx.strokeStyle = `rgba(200, 230, 255, ${s.life})`;
ctx.lineWidth = 1 * devicePixelRatio;
ctx.beginPath();
ctx.arc(s.x, s.y, s.radius, 0, Math.PI * 2);
ctx.stroke();
});
requestAnimationFrame(update);
};
update();
})();
</script>
</body>
</html>
Codex desarrolló una aplicación de una sola página bastante sólida, muy similar a la generada por ChatGPT.
Ventajas y límites de programar con IA
Para proyectos simples o cuando falta experiencia, la IA reduce días de trabajo a minutos. Es una gran ayuda para quienes están aprendiendo, aunque no sustituye la comprensión real.
Versiones gratuitas: ChatGPT vs Gemini
El resultado en versiones gratuitas fue muy similar en ambos casos.
Captura comparativa de Gemini y ChatGPT modelos Fast de Simulador de lluvia.

Código Gemini Instant:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simulador de Lluvia 2D</title>
<style>
/* CSS: Estilo visual y contenedor */
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #050505; /* Fondo oscuro */
}
canvas {
display: block;
}
#ui {
position: absolute;
top: 20px;
left: 20px;
color: rgba(255, 255, 255, 0.7);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
pointer-events: none; /* No interfiere con el mouse */
}
h1 { margin: 0; font-size: 1.2rem; font-weight: 300; letter-spacing: 2px; }
</style>
</head>
<body>
<div id="ui">
<h1>STORM SIMULATOR</h1>
<p>Partículas: <span id="count">0</span></p>
</div>
<canvas id="canvasLluvia"></canvas>
<script>
// JS: Lógica del simulador
const canvas = document.getElementById('canvasLluvia');
const ctx = canvas.getContext('2d');
const countDisplay = document.getElementById('count');
let gotas = [];
const numeroGotas = 500; // Cantidad de gotas simultáneas
// Ajustar el canvas al tamaño de la ventana
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
// Clase Gota
class Gota {
constructor() {
this.init();
}
init() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * -canvas.height; // Empiezan arriba de la pantalla
this.velocidadY = Math.random() * 10 + 10; // Velocidad de caída
this.longitud = Math.random() * 20 + 10; // Largo de la gota
this.opacidad = Math.random() * 0.5 + 0.2;
this.velocidadX = (Math.random() - 0.5) * 1; // Un poco de viento lateral
}
update() {
this.y += this.velocidadY;
this.x += this.velocidadX;
// Si la gota sale de la pantalla, se reinicia arriba
if (this.y > canvas.height) {
this.init();
}
}
draw() {
ctx.beginPath();
ctx.strokeStyle = `rgba(174, 194, 224, ${this.opacidad})`;
ctx.lineWidth = 1;
ctx.lineCap = 'round';
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.velocidadX, this.y + this.longitud);
ctx.stroke();
}
}
// Crear el array de gotas
function setup() {
gotas = [];
for (let i = 0; i < numeroGotas; i++) {
gotas.push(new Gota());
}
countDisplay.innerText = numeroGotas;
}
// Bucle de animación
function animate() {
// Fondo semi-transparente para crear el efecto de estela (motion blur)
ctx.fillStyle = 'rgba(5, 5, 5, 0.3)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
gotas.forEach(gota => {
gota.update();
gota.draw();
});
requestAnimationFrame(animate);
}
setup();
animate();
</script>
</body>
</html>
Código ChatGPT Instant:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Simulador de Lluvia 2D</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: linear-gradient(#0a0f1c, #1a233a);
font-family: sans-serif;
}
.rain {
position: absolute;
width: 2px;
height: 15px;
background: rgba(173, 216, 230, 0.8);
animation: fall linear infinite;
}
@keyframes fall {
0% {
transform: translateY(-20px);
opacity: 1;
}
100% {
transform: translateY(110vh);
opacity: 0;
}
}
.overlay {
position: fixed;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
color: #ccc;
font-size: 14px;
opacity: 0.6;
}
</style>
</head>
<body>
<div class="overlay">Simulador de lluvia 2D</div>
<script>
const rainCount = 300;
function createRainDrop() {
const drop = document.createElement('div');
drop.classList.add('rain');
drop.style.left = Math.random() * window.innerWidth + 'px';
drop.style.animationDuration = 0.5 + Math.random() * 1 + 's';
drop.style.height = 10 + Math.random() * 20 + 'px';
drop.style.opacity = Math.random();
document.body.appendChild(drop);
setTimeout(() => {
drop.remove();
}, 2000);
}
setInterval(() => {
for (let i = 0; i < rainCount / 10; i++) {
createRainDrop();
}
}, 100);
</script>
</body>
</html>
Probablemente el código provenga de repositorios similares. Lo llamativo es que Gemini, incluso en modo thinking, actúa como fast si no se le pide más explícitamente.
¿Es mejor una IA que otra?
En mi experiencia, OpenAI resulta más humano y natural. Gemini tiene gran potencia, pero menos integración directa. ChatGPT destaca por su ecosistema y herramientas web.
Conclusión
No se trata de qué IA es mejor, sino de entender cómo pedirle y cómo interpreta tu pedido. Este ejemplo es un caso claro de cómo no pedir código de forma ambigua.
En síntesis:
- Qué aprendes: cómo la proactividad, la interpretación del contexto y el nivel de literalidad del modelo afectan directamente al resultado del código.
- Cuándo usar cada IA: ChatGPT cuando buscas fluidez, iniciativa y menos fricción; Gemini cuando necesitas ejecución literal y control total del alcance.
- Error común a evitar: asumir que la IA “entiende” tu intención sin que definas correctamente el contexto o el nivel de detalle esperado.
Más adelante veremos desarrollos más complejos con vibe coding y diferencias reales entre desarrollo automático y asistido.
Gracias por leer el artículo. Si te gustó, deja tu comentario, síguenos en redes y forma parte de la comunidad VANTRA. Nosotros no seguimos el cambio, lo impulsamos.



Deja un comentario
Lo siento, tenés que estar conectado para publicar un comentario.