How do I update a d3 projection to match zoom tranform?

This is my zoom handler for my map:

const zoom = d3.zoom()
    .scaleExtent([1,25])
    .translateExtent([[width * -0.5, height * -0.5], [width * 1.5,height*1.5]])
    .on('zoom', (ev) => {
       svg.selectAll('path').attr('transform', ev.transform);  
    })

It updates the paths in the svg using the transform params from the event. This works great, but if I use projection(point) or similar methods to return the x,y coordinates of a point, then they will be incorrect.

I realise I need to update my projection to update the zoom/pan behaviour.

If I record the original map translation before any zooming, const origTrans = projection.translate(); and then apply the x,y transforms then I am able to correctly sync the projection for the top zoom level (ie k=1).

.on("end", (ev)=> {
  projection.translate([origTrans[0] + ev.transform.x * ev.transform.k, origTrans[1] + ev.transform.y * ev.transform.k]);
  const c =  projection([-3.3632, 55]);
  svg.append("circle")
    .attr("cx", c[0])
    .attr("cy", c[1])
    .attr("r", 9)
    .attr("fill", "red");
  }); 

I’m unclear as how zoom level relates to the projection scale. I can’t achieve the same thing

I’ve tried a few things e.g. – projection.scale(ev.transform.k), or projection.scale(projection.scale() * ev.transform.k) – I’m assuming there’s a lot more to it? If it helps I am using geoMercator for the projection.