JSFiddle - React, Tailwind, and code Playground

HTML

<canvas id=c></canvas>

CSS

#c {
  transform: scale(1, -1);
  outline: 1px solid;
}

JavaScript

class Graph {
  canvas
  startX
  endX
  startY
  endY
  samples
  funcs = []
  colors = ["red", "blue", "green", "orange"]
  
  constructor(canvas, startX, endX, startY, endY) {
    this.canvas = canvas
    this.startX = startX
    this.endX = endX
    this.startY = startY
    this.endY = endY
    this.initSamples()
  }
  initSamples() {
    const n = this.canvas.width
    const dx = (this.endX - this.startX) / n
    this.samples = [
      ...range(0, n).map(
        i => [i + 0.5, this.startX + i * dx]
      )]
  }
  add(func) {
    this.funcs.push(func)
  }
  repaint() {
    const ctx = this.canvas.getContext("2d")
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
    for (let i = 0; i < this.funcs.length; i++) {
      this.draw(ctx, this.funcs[i], this.colors[i % this.colors.length])
    }
  }
  draw(ctx, fn, color) {
    ctx.beginPath()
    let m = "moveTo"
    let scaleY = this.canvas.height / (this.endY - this.startY)
    for (const [px, x] of this.samples) {
      const y = fn(x)
      const py = (y - this.startY) * scaleY
      ctx[m](px, py)
      m = "lineTo"
    }
    ctx.strokeStyle = color
    ctx.stroke()
    ctx.closePath()
  }
}

function randomColor() {
  return "#" + Math.floor(Math.random() * 0x1000000).toString(16)
}

function *range(start, end) {
  for (let i = start; i < end; i++) yield i
}
range.prototype.map = function *(f) {
  for (let v of this) {
    yield f(v)
  }
}

const g = new Graph(document.querySelector('#c'), -2 * Math.PI, 2 * Math.PI, -1, 1)
g.add(Math.sin)
g.add(Math.cos)
g.repaint()