Some tips from having done this before, back before WebGL and HTML5 game engines were a thing:
60fps+ is doable, even on mobile, even without WebGL.
Use canvas. Anything else will often cause the browser to try to recalculate flow, which is very expensive. Rendering to canvas is comparatively cheap.
Store the layers (or the whole map, if that works) as images in memory. Rendering individual tiles to canvas adds up and ends up slow, rendering part of an image is cheap. My game had parallax so every layer had to be a separate layer. I rendered the tiles of each layer to an interim canvas at load time, stored the contents of that canvas in memory as an image, and rendered from it as needed. You can load images directly if you wish, though a tileset and map data is probably more compact, and the extra pre-rendering work you need to do at loadtime is probably faster than loading a larger image file from the server.
When rendering the layer images, only render the part that actually shows up in the viewport. Overdraw is expensive, don’t draw what isn’t seen. It’s cheaper to calculate what you do need than to render what you don’t need.
Similarly, when drawing any dynamic entities, don’t draw any that are entirely off-screen.
If you need very large maps, storing the entire layer may run you out of memory. Plus, canvas can only store images up to certain sizes, and the limitations are lower on mobile. If you run into this issue, split your map into overlapping sections (the overlap should be at least enough to fill the screen), and dynamically render the next segment as needed, and dump the old one. You can probably use WebWorkers to do this in the background. I haven’t needed to do it myself so I can’t help with implementation details. Overlapping sections ensure that you only ever need to store/render one at a time.
You can also improve performance significantly by reducing how much you draw, i.e. by reducing your viewport size. Pixel art is great for this, as you can render the game small, and then upscale it to almost fill the screen with CSS and the chunky pixels can still look good. That doesn’t work so well with non-pixel art, as it just looks blurry or blocky. Upscaling with CSS does have a performance penalty compared to rendering small, but it’s much less than drawing all those extra pixels yourself.