Please excuse our look. We're just getting started here.

Want to learn more about Twilio Forums? Check out our FAQ page here.

Twilio Video JavaScript V2 - Aspect Ratio Defaults to 640 480


Twilio Video JavaScript defaults to Width 640 and Height 480 although the code specifies 1280 and 720 for a HD laptop camera and for a 1980 & 1080p camera.

Has anyone seen that bug?

What to do?



  • How are you specifying the resolution in your code?

  • Hello Nash,

    To connect to a small Twilio room I use the following code:

    const token = await this.getAuthToken(participant);

       room = await connect(token, {



    video: { height: 720, frameRate: 24, width: 1280 },


    bandwidthProfile: {

         video: {

           mode: 'collaboration',

           dominantSpeakerPriority: 'high',



         dominantSpeaker: true,


    networkQuality: {local:1, remote: 1}

       } as ConnectOptions);

  • And this the report from Google Chrome developer tools:

    {group: "quality", name: "stats-report", payload: {,…}, timestamp: 1645474146332, type: "event",…}

    group: "quality"

    name: "stats-report"

    payload: {,…}

    audioTrackStats: [{trackId: "8a3f9e5f-5fa5-41e1-af67-2557dde51b10", trackSid: "MT0cf01aac8b084ed8ef689aaef447149f",…}]

    localAudioTrackStats: [{trackId: "15e75dec-9cc2-4c5b-bcd8-154bb190d2ca", trackSid: "MT4b2e5c126b6c3761f6d9346452e45e03",…}]

    localVideoTrackStats: [{trackId: "076178c1-04a7-48ea-aabc-466ef172c73d", trackSid: "MT37db7447b49b2ddf9f37afc9ab8f40f4",…}]

    0: {trackId: "076178c1-04a7-48ea-aabc-466ef172c73d", trackSid: "MT37db7447b49b2ddf9f37afc9ab8f40f4",…}

    bytesSent: 120467586

    captureDimensions: null

    captureFrameRate: null

    codec: "VP8"

    dimensions: {width: 640, height: 480}

    height: 480

    width: 640

    frameRate: 31

    packetsLost: 560

    packetsSent: 113870

    roundTripTime: 86

    ssrc: "700454453"

    timestamp: 1645474146328

    trackId: "076178c1-04a7-48ea-aabc-466ef172c73d"

    trackSid: "MT37db7447b49b2ddf9f37afc9ab8f40f4"

    peerConnectionId: "fe83f4c5-15a0-4483-a1a6-798c145b9b2a"

    videoTrackStats: [{trackId: "b87f2b3b-02d8-496d-a9aa-9a391b32ee0d", trackSid: "MT43846f4fe1ec246d73cb32bae4cc5a13",…}]

    0: {trackId: "b87f2b3b-02d8-496d-a9aa-9a391b32ee0d", trackSid: "MT43846f4fe1ec246d73cb32bae4cc5a13",…}

    bytesReceived: 88212458

    codec: "VP8"

    dimensions: {width: 640, height: 480}

    height: 480

    width: 640

    frameRate: null

    packetsLost: 191

    packetsReceived: 83794

    ssrc: "2004995826"

    timestamp: 1645474146328

    trackId: "b87f2b3b-02d8-496d-a9aa-9a391b32ee0d"

    trackSid: "MT43846f4fe1ec246d73cb32bae4cc5a13"

    session: "902cf5ec-f6af-4ea2-a919-cd8af01805d9"

    timestamp: 1645474146332

    type: "event"

    version: 1

  • To create a room I use

     const tracks = await Promise.all([createLocalAudioTrack(),createLocalVideoTrack()]);

  • Do I need to pass video arguments on createLocalVideoTrack or the connect I sent before is sufficient?

  • I can see you're passing both a tracks option to the connect options as well as a video option. tracks will override video , so how are you getting those tracks initially?

  • pnash
    pnash mod
    edited February 23

    I think I started asking my question before you posted your last update.

    If you are creating the video tracks before you connect to the room using in createLocalVideoTrack then you will need to pass the media constraints that you want to that function.

    While you are working on this, I would likely expand the video media constraints to make it easier for browsers to fulfil your request while also giving you a range to work within. You can do this by using min, max, and ideal to set the boundaries for the resolution you want. For example:

    const videoTrack = await createLocalVideoTrack({
      height: { ideal: 720, min: 480, max: 1080 },
      width:  { ideal: 1280, min: 640, max: 1920 },
      aspectRatio: 1.77777777778,
      frameRate: 24

    Do make sure you handle the OverconstrainedError in case you are in a browser that cannot handle the constraints you are asking for. That is normally a problem when using exact constraints, rather than min, max, and ideal, but it's good to handle just in case.

  • Thank you so much. I'll try your solution and let you know.

    All the best

  • Hello,

    I applied the changes but the height and width are still the same.

    Here is the code I used.

    private async createRoom(room: IRoom) {;

      const tracks = await Promise.all([createLocalAudioTrack(),createLocalVideoTrack(


          height: { ideal: 720, min: 480, max: 1080 },

          width: { ideal: 1280, min: 640, max: 1920 },

          aspectRatio: 1.77777777778,

          frameRate: 24




      this.activeRoom = await this.videoChatService.joinOrCreateRoom(, room.participant, tracks);





    Part of VideoChatService ===================

    async joinOrCreateRoom(name: string, participant: string, tracks: LocalTrack[]) {

      let room: Room = null;

      try {

       const token = await this.getAuthToken(participant);

       room = await connect(token, {



        dominantSpeaker: true,

       } as ConnectOptions);

      } catch (error) {

       console.error(`Unable to connect to Room: ${error.message}`);

      } finally {

       if (room) {




      return room;


    ================Part of Google Chrome Dev Tools showing the height and width used by Chrome:

    1. payload: {,…}
    2. audioTrackStats: [{trackId: "826de08a-49fa-4cdb-b837-214f0523ff07", trackSid: "MTc8f7d39575117ff7befcd732cf7c04b9",…}]
    3. localAudioTrackStats: [{trackId: "00eaa676-796c-4eae-a125-d08b89c4db06", trackSid: "MT8e1b6ca490e537d0f2950d76f27d1026",…}]
    4. localVideoTrackStats: [{trackId: "20946190-2d1a-4122-9288-b98e1089fc1f", trackSid: "MT0c60c2f69ee6ce60e01eaf03e5c6b04b",…}]
    5. peerConnectionId: "0f00a030-4491-49bd-8b37-853c8a2b7b1a"
    6. videoTrackStats: [{trackId: "cc60b124-d8dc-4941-8dff-50ac088ff658", trackSid: "MTdc44ff77b7ae2e86d0a5a353f07f1f99",…}]
    7. 0: {trackId: "cc60b124-d8dc-4941-8dff-50ac088ff658", trackSid: "MTdc44ff77b7ae2e86d0a5a353f07f1f99",…}
    8. bytesReceived: 374129
    9. codec: "VP8"
    10. dimensions: {width: 640, height: 480}
    11. frameRate: null
    12. packetsLost: 0
    13. packetsReceived: 370
    14. ssrc: "1062797222"
    15. timestamp: 1645491172331
    16. trackId: "cc60b124-d8dc-4941-8dff-50ac088ff658"
    17. trackSid: "MTdc44ff77b7ae2e86d0a5a353f07f1f99"
    18. session: "385f39e4-b6cf-492c-b1dd-c1d5839d86b6"
    19. timestamp: 1645491172346
    20. type: "event"
    21. version: 1

  • I did a new test setting the min to 720 for height and 1280 for width and ended with an Overconstrained error.

    height: { ideal: 720, min: 480, max: 1080 },

    width: { ideal: 1280, min: 640, max: 1920 },


    main-es2015.3ce8703b9199aa632c7d.js:1 ERROR Error: Uncaught (in promise): OverconstrainedError: {}

      at T (polyfills-es2015.0e5f8ae0f93963ba3cc4.js:1:33748)

      at polyfills-es2015.0e5f8ae0f93963ba3cc4.js:1:32827

      at a (main-es2015.3ce8703b9199aa632c7d.js:1:2863746)

      at u.invoke (polyfills-es2015.0e5f8ae0f93963ba3cc4.js:1:24369)

      at Object.onInvoke (main-es2015.3ce8703b9199aa632c7d.js:1:2224348)

      at u.invoke (polyfills-es2015.0e5f8ae0f93963ba3cc4.js:1:24309)

      at (polyfills-es2015.0e5f8ae0f93963ba3cc4.js:1:19781)

      at polyfills-es2015.0e5f8ae0f93963ba3cc4.js:1:34544

      at u.invokeTask (polyfills-es2015.0e5f8ae0f93963ba3cc4.js:1:24987)

      at Object.onInvokeTask (main-es2015.3ce8703b9199aa632c7d.js:1:2224164)

  • Hello,

    It's working now with the configuration you proposed.

    Thank you very much.

  • That was a bit of a journey! I'm glad it's working now. I was going to suggest that perhaps the camera you are requesting this from did not support more than 640x480, but if you got it to work it must have been something else.

    I'd love to understand what happened here, so if you know and don't mind, could you share why your first attempt with the new constraints didn't work and what you did to make it work?

  • Hello Nash,

    The first attempt failed simply because we create a local video track from two "places" in our code depending on the business logic.

    We forgot to update the second "place" where we use createLocalVideoTrack.

    The instructions are the same and are the ones you sent.

    It's working well now.

    Many thanks for your help.

  • Ah awesome. Glad it's working and you discovered what was going on!

  • Bod
    edited March 15

    Hello @pnash ,

    My webcam remains active after stopping it with the following Angular code.

    public closeMeeting(aRoom: Room): void {

    this.aRoom.localParticipant.videoTracks.forEach((publication) => {





    this.aRoom = null;


    Thanks you for your help


If this is an emergency, please contact Twilio Support. This is not an official Support channel.
Have an urgent question?
Please contact Twilio Support. This is not an official Support channel.
Contact Support