2 1/2 cups whole milk
1/2 cup granulated sugar
1/3 cup unsweetened cocoa powder (not Dutch process)
2 tablespoons cornstarch
4 ounces semisweet chocolate, chopped, plus more for garnish, optional
2 teaspoons pure vanilla extract
2 tablespoons unsalted butter
1 cup whipping cream
1/4 cup confectioners' sugar
Whisk the milk, granulated sugar, cocoa and cornstarch constantly in a nonreactive medium saucepan over medium-high heat until the cocoa has fully dissolved and the mixture starts to simmer, about 8 minutes. Reduce the heat to medium, and continue to cook, whisking, until the mixture thickens to the consistency of a light gravy, 6 to 7 minutes more. Stir in the chopped chocolate, vanilla and a pinch of salt and continue to cook, whisking, until the chocolate has melted and the pudding is thick and creamy, about 2 minutes more. Remove the pudding from the heat, add the butter and stir until the butter has melted.
Divide the pudding evenly into six 6- to 8-ounce ramekins or glasses. Cover with plastic wrap, and refrigerate for 4 hours or overnight until cold and fully set.
Before serving, pour the cream and confectioners' sugar into a chilled medium bowl. Whip the cream with a whisk or an electric mixer until soft peaks form, 2 to 3 minutes. Top each pudding with a dollop of whipped cream and chopped-chocolate garnish if using.