JSFiddle - React, Tailwind, and code Playground

by jcubed111

HTML

<canvas id="main" width=800 height=600></canvas>
<br>
<label>
    <span>xVel</span>
    <input type="range" id="xVel" min=0 max=10 value=7 step=0.1>
    <span class="value" id="xVelDisp">0.15</span>
</label>
<label>
    <span>fullG</span>
    <input type="range" id="fullG" min=0 max=100 value=60 step=1>
    <span class="value" id="fullGDisp">0.15</span>
</label>
<label>
    <span>partialG</span>
    <input type="range" id="partialG" min=0 max=100 value=15 step=1>
    <span class="value" id="partialGDisp">0.15</span>
</label>
<label>
    <span>maxJumpHeight</span>
    <input type="range" id="maxJumpHeight" min=0 max=10 value=6.4 step=0.1>
    <span class="value" id="maxJumpHeightDisp">0.15</span>
</label>
<label>
    <span>jumpBuffer</span>
    <input type="range" id="jumpBuffer" min=0 max=1 value=0.4 step=0.05>
    <span class="value" id="jumpBufferDisp">0.15</span>
</label>

SCSS

@import url('https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@300&display=swap');

body{
    background: #223;
    font-family: 'Josefin Sans', sans-serif;
    font-size: 18px;
    font-weight: 200;
    color: #ddd;
}

label{
    display: block;
    span {
        display: inline-block;
        width: 125px;
        vertical-align: middle;
        text-align: right;
    }
    input {
        width: 200px;
        height: 25px;
        vertical-align: -55%;
    }
    span.value {
    text-align: left;                
    }
}

JavaScript

let canvas = document.getElementById('main');
let ctx = canvas.getContext('2d');
ctx.setTransform(1, 0, 0, -1, 0, 600);

const scale = 40;

function renderBack() {
    ctx.clearRect(0, 0, 800, 600);
    ctx.beginPath();
    ctx.strokeStyle = '#fff1';
    for (let z = 0; z < 600; z += scale) {
        ctx.moveTo(0, z + .5);
        ctx.lineTo(800, z + .5);
    }
    for (let x = 0; x < 800; x += scale) {
        ctx.moveTo(x + .5, 0);
        ctx.lineTo(x + .5, 600);
    }
    ctx.stroke();
}

function startV(d, b, g) {
	return Math.sqrt(2*b**2*g**2 - 2*g*d) + 2*b*g;
}

function render(xVel, fullG, partialG, maxJumpHeight, jumpBuffer) {
	// jumpBuffer: number of seconds of a = 0 at beginning of jump
    renderBack();

    const c = ({
        x,
        z
    }) => [x * scale, z * scale];

	['vel', 'pos'].forEach(l => {
        [0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0].forEach(jumpDuration => {
            const dt = 1 / 1000;
            let pos = {
                x: 2,
                z: 1
            };
            let vel = {
                x: xVel,
                // z: Math.sqrt(jumpBuffer**2 * partialG**2 - 2*maxJumpHeight*partialG) + jumpBuffer*partialG
                z: startV(maxJumpHeight, jumpBuffer, partialG),
            };
            const value = () => l == 'vel' ? {x: pos.x, z: vel.z} : pos;
            ctx.strokeStyle = l == 'pos' ? '#0af' : '#70b';
            ctx.beginPath();
            ctx.moveTo(...c(value()));
            for (let t = 0; t < 10.0; t += dt) {
                let pressingUp = t < jumpDuration;
                let az;
                if(pressingUp && t < jumpBuffer) {
                    az = -partialG;
                }else if(pressingUp){
                    az = partialG;
                }else{
                    az = fullG;
                }
                pos.x += vel.x * dt;
                pos.z += vel.z * dt + 0.5 * az * dt * dt;
                vel.z...